C / malloc/自由腐败一般问题



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

所以我有一个程序,一个malloc,后面跟着一些复杂的代码,后面跟着一个自由的。在复杂代码的某个地方,内存被双重自由或越界写入破坏(两者都在与原始malloc分开的内存区域中)。这将导致最初的自由失败。此行为是相当确定的。

我有几个问题:

    内存损坏影响这样一个单独的内存区域的最小条件是什么?
  1. 是否可以采取任何积极的措施来防止这种交叉腐败?
  2. 使用指针算术在连续分配的内存之间来回跳转是否定义为标准行为?

    /* 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种常见的场景:对象下溢、对象溢出、悬空引用或野存储。

对象下流发生在写字节时正好在分配的块之前。这些字节通常包含关键的块链接,并且损坏通常是严重的。

对象溢出发生在写字节正好在分配块之后。通常在末尾有少量填充,所以一两个字节通常不会造成严重损害。如果你继续写下去,你最终会写到一些重要的东西,就像underflow一样。

悬空引用意味着通过一个曾经有效的指针进行写入。它可以是指向超出作用域的局部变量的指针,也可以是指向已释放的块的指针。

野存储意味着写入一个地址远超出分配块。这可能是一个小的正地址(比如指针值为0x20),在调试环境中,这些区域通常可以受到保护,或者它可能是随机的垃圾,因为指针本身已经损坏。这些不太常见,但很难发现和修复。

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

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

那么你可以使用外部工具。一种是有效的,但并不总是可行的。在一个案例中,我们编写了一个完整的堆日志系统来跟踪每个分配,以发现类似的问题。

第三季。第二个示例不能保证第2行body_struct的正确对齐。根据C标准n1570 S7.22.3,由malloc()返回的内存适合被对齐用作指向任何对象的指针。编译器会按照这个假设来布置结构体。

但是,该要求不适用于结构体数组的成员。像这样的结构体的数组的第二个成员是否对齐是由实现定义的。

struct s {
  double d;
  char c;
} ss[2];
考虑到这一点,您的代码是有效的C语言,但可能具有实现定义的或未定义的行为,这取决于对齐要求。当然不推荐。

(1)任何未定义的行为都可能导致内存损坏,包括但不限于写入不属于对象的任何内存位置。

(2)仔细编写代码:-(

)

(3)第二次赋值是不可移植的,并且由于对齐问题可能导致各种问题。为了使其正确和可移植,通常使用灵活的数组成员。如果你总是分配一个头和一个体,定义一个新的结构体

typedef struct {
    header_struct header;
    body_struct body;
} body_plus_header_struct;

如果你分配一个头和一个可变数量的主体,写

typedef struct {
    header_struct header;
    body_struct bodies [];
} body_plus_header_struct;

这里,body_plus_header_struct有一个保证四舍五入的大小,以便body数组的地址有正确的对齐方式。要为n个体分配一个结构体,请分配

body_plus_header_struct* p = malloc (sizeof (*p) + n * sizeof (p->bodies [0]));

最新更新