C malloc/free corruption 一般问题

标签 c pointers memory memory-management

这个问题类似于c malloc questions (mem corruption)但我又问了一遍,因为我想要比所提供的更具体的信息。

所以我有一个带有一个 malloc 的程序,后面是一些复杂的代码,然后是一个 free。在复杂代码的某处,内存因双重释放或越界写入而损坏(两者都位于与原始 malloc 不同的内存区域)。这会导致原始 free 失败。这种行为是相当确定的。

我的问题有几个:

  1. 内存损坏影响像这样的单独内存区域的最低条件是什么。
  2. 是否可以采取任何主动措施来防止这种交叉腐败。
  3. 使用指针运算在连续分配的内存之间来回跳转是否符合标准定义的行为。

    /* 3 example */
    void *m = malloc(sizeof(header_struct) + sizeof(body_struct));
    body_struct *b = (body_struct*) (((header_struct*)m)+1);
    header_struct *h = (header_struct*) (((header_struct*)b)-1);
    

最佳答案

好问题。

Q1。 标准下的最低条件是触发未定义行为的任​​何条件。不幸的是,这是一个相当长的列表并且不可操作。在实践中,该列表归结为 4 种常见情况:对象下溢、对象溢出、悬垂引用或野外存储。

当您写入分配 block 之前的字节时,会发生对象下溢。这些字节包含关键 block 链接是很常见的,并且损坏通常很严重。

当您在分配的 block 之后写入字节时,就会发生对象溢出。末尾通常有少量填充,因此一两个字节通常不会造成严重损坏。如果你继续写,你最终会遇到一些重要的事情,根据下溢。

悬挂引用意味着通过一个曾经有效的指针进行写入。它可以是指向超出范围的局部变量的指针,或者指向已释放的 block 的指针。这些很讨厌。

Wild store 意味着写入分配 block 之外的地址。这可能是一个小的正地址(比如指针值为 0x20)并且在调试环境中这些区域通常可以受到保护,或者它可能是随机垃圾,因为指针本身已损坏。这些不太常见,但很难找到和修复。

Q2。调试堆是您的第一级保护。它会检查链接并将特殊模式写入未使用的空间,通常有助于发现和修复问题。如果您使用的是调试堆,则 free() 通常会触发一些诊断事件,但您通常可以找到其他一些调用来执行相同的操作。在 Windows 上是 HeapValidate()。

您可以通过使用守卫/哨兵(查找)和您自己的堆检查功能实现您自己的堆来做更多事情。除此之外(至少在 C 中),你只需要在编写代码方面做得更好。您可以添加断言,这样至少代码可以快速失败。

然后就可以使用外部工具了。一种是 valgrnd,但它并不总是可行的。在一个案例中,我们编写了一个完整的堆日志系统来跟踪每个分配,以发现此类问题。

Q3。您的第二个示例不保证第 2 行中 body_struct 的正确对齐。根据 C 标准 n1570 S7.22.3,malloc() 返回的内存适合对齐用作指向任何对象的指针。编译器将根据此假设布置结构。

但是,该要求不会扩展到结构数组的成员。像这样的结构数组的第二个成员是否对齐是实现定义的。

struct s {
  double d;
  char c;
} ss[2];

考虑到这一点,您的代码是有效的 C,但可能具有实现定义或未定义的行为,具体取决于对齐要求。当然不推荐。

关于C malloc/free corruption 一般问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24855927/

相关文章:

c++ - 共享库中的全局变量如何链接?

c - 在多进程环境中读取文件时意外的 EOF

将 4 个整数的结构转换为 float

c++ - C++中的指针。什么类型的对象

memory - 当我的 iOS 游戏进入后台模式时,它是否应该释放内存?

CS50 Speller,通过测试,但 check50 问题,使用未初始化的值

memory - OpenCL 何时使用全局、私有(private)、本地、常量地址空间

android - android函数内部泄漏 'setTextColor'

c - 检查字符串卡住的函数