C/C++ 恨透了 double free or corruption

*以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/IwSVImp5cOB3gZbaf0YiPw

写过 C/C++ 的都知道,内存允许程序员自主分配,用完了这些资源也得释放出来,这种在系统运行过程中动态申请的内存,称为动态内存。

常言道,借东西好借好还,下次再借也不难,但是有的人有时候还真的忘了还回去。这要是发生在程序运行时,申请的内存没正常释放,没管理好,就避免不了会面对内存报错的问题。

内存都允许你自由操纵了,灵活性是真的大,恰恰这也是它的弊端。

今天就来聊聊 C/C++ 的报错 double free or corruption

怎么分配和释放内存?

C 语言提供了两个函数用于分配和释放内存 malloc 和 free,需要引用头文件 <stdlib.h>。<stdlib.h> 是 C 标准库头文件 为 C 语言程序员提供可靠、高效的函数,以实现动态内存分配、数据类型转换、伪随机数生成、过程控制、搜索和排序、数学以及多字节或宽字符函数,还包括一些常用常数,目的是促进组织和平台间的代码标准化。

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int *ptr = malloc(sizeof(int));
    *ptr = 100;
    printf("%d", *ptr);
    free(ptr);
    return 0;
}

输出:

100

调用 malloc 会分配一块内存空间,并将这块内存空间的首地址返回。调用时,需要传入目标内存空间的大小,单位按照字节(Byte)算,而返回的地址数据类型是 void*,所以,根据目标空间的具体用途转换即可。

这块内存空间在分配之后还属于未初始化的状态,如果对内存空间的使用比较复杂,建议先用 memset 初始化一下。

内存空间使用完,需要使用 free 释放掉,避免闲置浪费,否则就算是内存泄漏了。内存泄露会直到程序进程结束为止。

在其它的高级语言里,比如 Java、Python 等,出于内存安全的考虑,都不会允许用户自己管理内存,而 C++ 是个例外,这可能来自于 C 语言的传承。

C++ 里同样提供了 malloc 和 free,但是引用的头文件变成了 是 <stdlib.h> 增强版,而且所有内容都在命名空间内声明,所以使用前必须通过命名空间引用。

另外 C++ 还提供了两个额外的操作符用于分配和释放内存,分别是 new 和 delete。

#include <iostream>
using namespace std;

int main()
{
    int *ptr = new int;
    *ptr = 100;
    cout << *ptr << endl;
    delete ptr;
    return 0;
}

输出:

100

关键词 new 后接上一个数据类型,然后分配和数据类型 int 对应大小的内存空间,并返回首地址。对应地,new 申请的内存空间被使用完不再需要时,应该使用关键词 delete 释放,delete 直接操作内存空间首地址。

出现 double free or corruption Error

借来的钱用得可以很爽,是的,常人都这样。不过,每到要还钱的时候就特别不情愿,要么推三推四,要么直接抵赖,一不留神就忘了是否有还过这事。

比如,张三本来一直在外租房将就着过日子,随着家里人口逐渐增多,就和老婆合计着从银行贷了一笔资金准备买房嘛,贷了款之后,银行贷款经理就告诉他,“张先生,你们家以后每月就得由一名代表人来还贷款,不需要几个人同时还的,记住了哈!”

好了,这个故事给了我们什么启发呢?就是资金或者资源的借入借出需要有一个管理人,这样可以避免混乱进而出错。

同样的,在 C/C++ 的编程里边,经常会出现一些内存资源管理混乱而出现的报错甚至运行时崩溃的问题,比如 double free or corruption。

#include <iostream>
using namespace std;

int main()
{
    int *ptr = new int;
    *ptr = 100;
    cout << *ptr << endl;
    delete ptr;
    delete ptr;
    return 0;
}

执行

100
free(): double free detected in tcache 2
Aborted (core dumped)

程序执行崩溃并报错 double free,根本原因是对同一内存地址调用了多次的 free 或 delete 执行释放,这会导致应用的内存管理数据结构被损坏,甚至会允许恶意用户在内存任意区域写入数据。这类损坏会导致程序崩溃或者程序的部分执行流程被改变。如果攻击者这个时候特意覆盖特定的寄存器或者内存区域来引导执行他们的代码,进而可以产生提升权限的交互式 shell,这样就完全被破防了。

这也算是内存泄漏的一种,系统一旦检测到 double free 也会终止进程继续执行(Aborted)。

内存被释放之后会发生什么?

一块内存被释放之后,空闲的内存会被放入链表中,用于重新管理和组合不同的空闲内存碎片,便于将来用于分配更大的内存空间。这个链表属于双向链表,每个空闲的内存空间都可以往前和往后查找其它内存空间。

那么攻击者可以利用这个过程吗?

答案是肯定的。当 free 被调用时,攻击者可以让原本需要被链表管理的空闲内存取消链接,覆盖寄存器值并从缓冲区载入shell代码,最终往内存写入任意值。

常见的触发情形

上面的示例代码简单演示了 double free 的触发,平常出现这种报错的条件并不比上面的情形要复杂多少。比如,释放同一块内存的动作在相隔了几百甚至更多行的位置执行,有的还发生在不同源码文件,这就会让程序员容易多次释放。下面尝试总结一下,来看一下常见的犯错情形:

  1. 释放前判断的条件错误或者其它不常见的情况
  2. 内存被释放后还在使用
  3. 内存释放的管理责任方混乱

如何避免

其实,细看一下上面总结的几种常见犯错情形,我们也可以很好地避免低级错误。

有个最佳实践是,分配的内存地址存储变量 ptr 在定义声明时就应该初始化为 NULL,内存被释放后应立刻将 ptr 置为 NULL,使用这块内存或者释放前应该遵循先判断内存空间是否有效的原则,简单点可以用 (ptr != NULL)。

另外,负责释放的管理责任方应该尽量单一,即使横跨多个源文件或模块。这里有个道理就是避免”多龙治水“。

C/C++ 恨透了 double free or corruption

中国在过去一直是个农业大国,有着重农轻商的历史,各种典故都有着农业的影子。

相传,几龙治水、几牛耕地那是对当年农业收成的预示,不妨翻一下老黄历看看? “龙”是管雨的神,以五龙治水可获风调雨顺,因东南西北中都有神龙,各施其职。龙少了当年就要发大水;龙多了当年将要天大旱。原因是管雨的龙神少了怕管不过来,就忙忙碌碌四处播雨以至大涝;管雨的龙神多了呢,就像“三个和尚无水吃”一样以至大旱。至于涝到什么程度还看治水的龙少到什么程度,龙越少涝得越严重。旱的程度亦一样。

因此就有了“龙多不下雨”的谚语。

计算机编程说到底还是程序员的思维体现,人情世故也会反映在代码的逻辑上。

原文链接:https://www.cnblogs.com/englyf/p/17255769.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C/C++ 恨透了 double free or corruption - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • 魔鬼鱼DR7-PLUS做工值得买吗?未来人类魔鬼鱼DR7-PLUS配置性能全面评测+拆解

    魔鬼鱼DR7-PLUS做工值得买吗? 魔鬼鱼DR7-PLUS是一款高性能台式电脑主机,其做工和性能非常出色,下面我们来详细地评测一下它的配置和性能。 配置 魔鬼鱼DR7-PLUS的配置如下: 处理器:Intel Core i9-11900K(3.5GHz) 显卡:NVIDIA GeForce RTX 3090 24GB 内存:32GB DDR4 3200MH…

    C 2023年5月24日
    00
  • C语言容易被忽视的函数设计原则基础

    我来详细讲解一下“C语言容易被忽视的函数设计原则基础”的攻略。 1. 函数设计原则的重要性 函数是程序中最重要的组成部分之一,良好设计的函数可以增强程序的可读性、可维护性、可扩展性和可重用性。函数设计原则是编写好函数的基础,而忽视这些基本的原则将会导致程序出现各种问题。编写出符合基本原则的函数,既能使程序更加健壮、高效,又能提高程序的可维护性和可读性。 2.…

    C 2023年5月23日
    00
  • 威联通301W路由器怎么样? 威联通301W拆机测评

    威联通301W路由器测评攻略 介绍 威联通301W是一款智能路由器,拥有多种配置选项和广泛的应用功能。本篇文章将从拆机、性能、易用性等多个方面对该路由器进行测评,为大家提供详细介绍。 拆机 首先,我们需要打开威联通301W路由器的外壳,了解内部构造。拆机步骤如下: 用螺丝刀将底部五颗螺丝去除 待底部外壳拆除后,用螺丝刀将顶部四颗螺丝去除 拆下顶部外壳,就可以…

    C 2023年5月23日
    00
  • C++实现高校教室管理系统

    C++实现高校教室管理系统 概述 本文介绍如何使用C++语言实现高校教室管理系统。本系统主要功能包括管理教室和预定教室。此外,本系统还支持多用户登录、权限管理以及数据持久化等功能。 系统需求: 管理员可以添加/删除/编辑教室信息 用户可以预定教室 支持多用户登录和权限控制 数据持久化 设计 数据结构 系统需要保存的数据主要有教室信息和用户信息。我们可以定义一…

    C 2023年5月23日
    00
  • python使用Apriori算法进行关联性解析

    下面详细讲解一下“python使用Apriori算法进行关联性解析”的完整攻略。 一、什么是关联性分析和Apriori算法 1.1 关联性分析 关联性分析(Association Analysis)是一种寻找事物之间依存关系的方法,是数据挖掘领域中的一种常用方法。在销售、广告、推荐等领域具有广泛的应用。 关联性分析的基本目的是找出每个物品之间的关系,比如商品…

    C 2023年5月23日
    00
  • Terry七月Ruby读书笔记(比较详细)第1/4页

    “Terry七月Ruby读书笔记(比较详细)第1/4页”攻略 1. 简介 “Terry七月Ruby读书笔记(比较详细)第1/4页”是一篇介绍Ruby编程语言的文章,主要分为四个部分,该攻略针对该文章第1/4页的内容进行详细讲解和分析。 2. 内容概述 在该笔记中,作者主要介绍了Ruby的基本数据类型和运算符。其中,介绍了Ruby的数字类型、字符串类型、布尔类…

    C 2023年5月23日
    00
  • Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例

    下面是详细讲解“Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例”的完整攻略,包括以下步骤: 1. 安装相关库 在使用Python操作sqlite和Excel文件之前,需要安装相应的库: sqlite3:Python内置库,用于操作sqlite数据库。 openpyxl:需要安装的Python库,用于操作Excel文件。 可以使用…

    C 2023年5月23日
    00
  • json 带斜杠时如何解析的实现

    在 JSON 中,斜杠 (/) 被视为特殊字符之一,用来转义其他字符,例如将双引号 (\”) 引用为字符串中的字符而不是字符串的结束标识符。JSON 规范定义,当转义序列被解码时,所有的反斜杠必须保留并被传播到最终结果中。 以下是处理 JSON 带斜杠的两条示例说明: 示例1:使用JSON.stringify()方法转换带斜杠的JSON 假设有一个 JSON…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部