最近遇到一个问题,很有意思,在此记录下,以备后续参考。
程序运行异常,报错:malloc: memory corruption.
用gdb 调试程序,bt 如下,程序在申请344 bytes内存时失败。
疑问:344bytes内存并不大,为何会失败呢?
进一步,打开AddressSanitizer,重选编译并运行程序,AddressSanitizer报错如下,报错内容分3部分贴出如下(信息安全,隐藏掉部分堆栈信息):
1. 重点:红色字体:heap-buffer-overflow on address 0xf3805ef0, 蓝色字体, WRITE of size 4 at 0xf3805ef0
解释:堆溢出,在*.c的211行对0xf3805ef0地址非法写,尝试写4个byte.
2.绿色字体:0xf3805ef0 is located 192 byte to the right of 688-byte regin [0xf3805b80,0xf3805e30)
在*.c的507行分配了688个byte - 对应内存区域[0xf3805b80,0xf3805e30), 程序非法访问的地址0xf3805ef0 位于此内存区域右侧的192个byte的位置。
3. Summary.
分析过程不必赘述,我把结论写一下:
1. 在*.c的507行分配了688个byte - 对应内存区域[0xf3805b80,0xf3805e30)
分析code, 发现这里是分配了2个结构体struct M,sizeof(struct M) = 344.
typedef struct M
{
...
int data;
...
} M_t
2. 在*.c的211行对0xf3805ef0地址非法写,尝试写4个byte.
查看code,在211行尝试写一个struct M的成员data, 而data成员之前结构体成员的长度为192.
注意cellGroupId的offset 和 size.
回味下面ASAN的提示:
0xf3805ef0 is located 192 byte to the right of 688-byte regin [0xf3805b80,0xf3805e30)
由此可以推测:程序动态分配了2个struct M, 由于code 存在bug, 却访问了第三个struct M.
最后进一步debug,发现程序确实是指针操作出错,访问了2个struct M后面的内存。
思考:malloc失败 与 AddressSanitizer: heap-buffer-overflow
没有开启AddressSanitizer时,程序写struct M的成员data时并没有直接报错,而是延迟到再次malloc内存时报错?
个人认为如下:
1. 程序非法写struct M的成员data,0xf3805ef0 地址位于堆上,此地址是可写的。(如果是访问.text代码段地址的话,程序是会马上终止的。)
2. malloc时,我推测内存管理模块会check将要分配出的内存区域是否正常。 怎么检查:比如,没有分配出的内存区域设置为默认值或者魔数,如果发现有其它的非法值存在,就发生了非法写,也就是说此内存还没有被分配出去,就已经有code 对其做了写操作。
根据之前内存池管理(https://blog.csdn.net/wowricky/article/details/83218126)的经验, 内存管理模块会在malloc和free时对内存区域进行检查,如果存在异常会直接报错memory corruption。所以malloc时的内存报错,通常是由于之前的内存越界访问导致的,这种问题通常比较难以定位。我碰到的这个问题比较幸运,非法访问地址0xf3805ef0 是前一个动态内存区域[0xf3805b80,0xf3805e30) 操作不当导致的。