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日

相关文章

  • 型号为a1526的iphone5c 联通版4g网络怎么开启 联通版iphone5c a1526越狱后破解4g教程

    那么针对这个问题,我将分为两个部分来进行回答。 如何开启型号为a1526的iphone5c联通版4G网络? 首先,您需要确认您的手机是否支持4G网络。型号为a1526的iphone5c 联通版是支持4G网络的,但需满足以下条件: 手机系统为iOS 8.0及以上版本 必须使用联通的USIM卡 在中国大陆地区开通4G网络服务 确认您的手机符合以上条件后,您需要进…

    C 2023年5月23日
    00
  • C++11实现简易定时器的示例代码

    下面是关于“C++11实现简易定时器的示例代码”的完整攻略。 标题 C++11实现简易定时器的示例代码 简介 在开发事件驱动或多线程程序时,经常需要一种定时器来控制任务的执行时间。本篇攻略将介绍如何使用C++11编写一个简易的定时器,以此来满足我们在各种场景中的需求。 本攻略将包含两个实例展示如何使用简易定时器,读者可以通过这两个实例学习如何将其应用于自己的…

    C 2023年5月22日
    00
  • C++实现单例模式的方法

    C++实现单例模式的方法可以通过以下两种方式实现: 1. 饿汉式单例模式 在饿汉式单例模式中,单例实例在程序启动时被立即初始化,它是线程安全的。具体实现如下: class Singleton { private: Singleton() {} static Singleton* m_instance; public: static Singleton* In…

    C 2023年5月23日
    00
  • C语言实现逆序输出详细

    当我们需要逆序输出一个字符数组或字符串时,我们可以使用C语言中的循环语句和数组下标实现。 首先,我们先定义一个字符数组或字符串,存储数据。接着,我们创建一个循环语句,从最后一个元素开始逆序输出到第一个元素。最后,我们在每个元素之间添加一个空格或其他特定符号,以便于人类阅读。 以下是完整的C语言实现逆序输出的攻略: 步骤如下: 1. 定义字符数组或字符串 我们…

    C 2023年5月23日
    00
  • C/C++ 中怎样使用SetConsoleTextAttribute()函数来控制输出字符的颜色

    当在控制台程序中使用C/C++语言输出字符时,通过SetConsoleTextAttribute()函数可以改变输出字符的颜色。该函数在Windows头文件中定义。下面给出SetConsoleTextAttribute()函数的用法及示例代码。 语法 BOOL SetConsoleTextAttribute( HANDLE hConsoleOutput, W…

    C 2023年5月23日
    00
  • gdb调试命令的使用及总结

    GDB调试命令的使用及总结 简介 GDB是一个功能强大的调试工具,可以用于调试C程序等各种编程语言。 它可以帮助程序员查找程序崩溃、调查内存问题、跟踪函数调用等问题。 本文旨在提供一些GDB常用调试命令的示例及使用方法,以便于程序员快速定位程序的问题。 命令列表 下面是一些常用的GDB调试命令的列表。 常用命令 命令 描述 run 运行程序 break [f…

    C 2023年5月22日
    00
  • win10系统出现0x000000c5蓝屏的修复方法

    Win10系统出现0x000000c5蓝屏的修复方法 前言 在Windows10系统运行过程中,可能会碰到各种各样的蓝屏错误。其中,0x000000c5蓝屏错误是较为常见的一种,表示驱动程序试图访问非法内存地址。这可能是因为驱动程序不能正确处理该内存地址,或者因为某些未知原因导致内存地址错误。本文将详细介绍修复0x000000c5蓝屏错误的步骤。 步骤 步骤…

    C 2023年5月24日
    00
  • C语言实现矩阵运算案例详解

    C语言实现矩阵运算案例详解 简介 矩阵是线性代数中非常重要的概念,也是很多领域中经常用到的数学工具。在计算机科学中,矩阵也得到了广泛的应用。在这篇文章中,我们将介绍如何使用C语言实现矩阵的基本运算,包括相加、相乘、转置、求逆等操作。我们将使用标准C语言来实现这些操作,不需要任何额外的库。 矩阵的基本操作 矩阵的表示 在讨论矩阵的操作之前,我们需要先了解矩阵的…

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