这个问题类似于c malloc问题(内存腐败),但我再次提出这个问题,因为我想要比提供的内容更具体的信息.
所以我有一个带有一个malloc的程序,然后是一些复杂的代码,然后是一个免费的.在复杂代码中的某处,内存被双重释放或越界写入(都在原始malloc的内存的不同区域中)破坏.这会导致原始的自由失败.这种行为是相当确定的.
我的问题是几个:
内存损坏会影响像这样的单独内存区域的最小条件是什么.
是否有任何可采取的主动措施来防止这种交叉腐败.
是否定义了使用指针算法在连续分配的内存之间来回跳转的标准行为.
/* 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);
david.pfx.. 5
好问题.
Q1.标准下的最小条件是触发未定义行为的任何事物.不幸的是,这是一个相当长的列表,不可行.在实践中,列表归结为4种常见场景:对象下溢,对象溢出,悬空引用或野生商店.
当您在分配的块之前写入字节时,会发生对象下溢.这些字节通常包含关键的块链接,并且损坏通常很严重.
当您在分配的块之后写入字节时,会发生对象溢出.最后通常会有少量填充,因此一两个字节通常不会造成严重损坏.如果你继续写作,你将最终击中重要的东西,如下溢.
悬空引用意味着通过曾经有效的指针进行写入.它可以是指向已超出范围的局部变量的指针,也可以是已释放的块的指针.这些都很讨厌.
Wild存储意味着写入分配块之外的地址.这可能是一个小的正地址(比如指针值为0x20),并且在调试环境中这些区域通常可以受到保护,或者它可以是随机垃圾,因为指针本身已经损坏.这些不太常见,但很难找到并修复.
Q2.调试堆是您的第一级保护.它将检查链接并将特殊模式写入未使用的空间,通常有助于查找和修复问题.如果你使用的是调试堆,那么free()通常会触发一些诊断活动,但你通常可以找到其他一些调用来做同样的事情.在Windows上是HeapValidate().
您可以通过使用警卫/哨兵(查看)和您自己的堆检查功能实现自己的堆来做更多事情.除此之外(至少在C中),您只需要更好地编写代码.您可以添加断言,以便至少代码快速失败.
然后你可以使用外部工具.一个是valgrnd,但并不总是可行的.在一个案例中,我们编写了一个完整的堆日志系统来跟踪每个分配,以找到这些问题.
Q3.第二个示例不保证第body_struct
2行的正确对齐.根据C标准n1570 S7.22.3,返回的内存malloc()
适合对齐以用作指向任何对象的指针.编译器将使用此假设来布局结构.
但是,该要求不会扩展到结构数组的成员.它是实现定义的,这样的结构数组的第二个成员是否对齐.
struct s { double d; char c; } ss[2];
考虑到这一点,您的代码是有效的C,但可能具有实现定义或未定义的行为,具体取决于对齐要求.当然不推荐.
好问题.
Q1.标准下的最小条件是触发未定义行为的任何事物.不幸的是,这是一个相当长的列表,不可行.在实践中,列表归结为4种常见场景:对象下溢,对象溢出,悬空引用或野生商店.
当您在分配的块之前写入字节时,会发生对象下溢.这些字节通常包含关键的块链接,并且损坏通常很严重.
当您在分配的块之后写入字节时,会发生对象溢出.最后通常会有少量填充,因此一两个字节通常不会造成严重损坏.如果你继续写作,你将最终击中重要的东西,如下溢.
悬空引用意味着通过曾经有效的指针进行写入.它可以是指向已超出范围的局部变量的指针,也可以是已释放的块的指针.这些都很讨厌.
Wild存储意味着写入分配块之外的地址.这可能是一个小的正地址(比如指针值为0x20),并且在调试环境中这些区域通常可以受到保护,或者它可以是随机垃圾,因为指针本身已经损坏.这些不太常见,但很难找到并修复.
Q2.调试堆是您的第一级保护.它将检查链接并将特殊模式写入未使用的空间,通常有助于查找和修复问题.如果你使用的是调试堆,那么free()通常会触发一些诊断活动,但你通常可以找到其他一些调用来做同样的事情.在Windows上是HeapValidate().
您可以通过使用警卫/哨兵(查看)和您自己的堆检查功能实现自己的堆来做更多事情.除此之外(至少在C中),您只需要更好地编写代码.您可以添加断言,以便至少代码快速失败.
然后你可以使用外部工具.一个是valgrnd,但并不总是可行的.在一个案例中,我们编写了一个完整的堆日志系统来跟踪每个分配,以找到这些问题.
Q3.第二个示例不保证第body_struct
2行的正确对齐.根据C标准n1570 S7.22.3,返回的内存malloc()
适合对齐以用作指向任何对象的指针.编译器将使用此假设来布局结构.
但是,该要求不会扩展到结构数组的成员.它是实现定义的,这样的结构数组的第二个成员是否对齐.
struct s { double d; char c; } ss[2];
考虑到这一点,您的代码是有效的C,但可能具有实现定义或未定义的行为,具体取决于对齐要求.当然不推荐.