刚刚花了一个星期磨出一来一个Planet的*.prj类型的一周项目,感觉收获很大,特此作出分享.
此项目是要解决一个Heap Corruption的问题,但是我们知道,通常情况下,当我们在堆中分配了一段内存,尽管在使用过程中可能出现了越界操作,但是系统在越界的一瞬间常常并不直接报错,而是在最后delete 时抛出一个Heap Corruption。这是因为操作系统的堆分配粒度是一个4k,若内存越界不是刚好在那个4k边界上,并不会引起操作系统的警觉而报错。
如果我们只是在Heap Corruption发生的时候察看call stack,会发现得不到多少有用的信息。在一份规模较大的代码中手工检查也
不是很现实。
这个时候,堆调试利器-PageHeap可以帮上大忙.此工具可以强迫程序在把每一块堆都分配在一个4k内存页的末尾位置,并标记下一块4k
紧邻内存为不可用,一旦越界,马上报错。但是当我一开始写下如下的代码进行测试时,却发现代码仍是在直到delete 时才报错:
char *pcTest = new char[10];
pcTest [10] = 1;
//...
//...
//...
delete []pcTest;
打开代码一看,当进行了new操作后,在debug模式下内存中实际
情况如下:
0x0342AFF0 cd cd cd cd cd cd cd cd
0x0342AFF8 cd cd fd fd fd fd d0 d0
下一块4k: ?? ?? ?? ?? ?? ?? ?? ??
10个cd是可使用的内存区,4个fd是debug模式下用来debug的,两个d0起占位的作用。因为内存分配的首地址必须是8的倍数,所以尽管pageheap尽量的把新分配的内存分配在4k末尾,但刚出界时是写在了那几个fd和d0上,并没有超越4k界。
若换作pcTest[16] = 1时,系统才认为是非法操作。综上所述,即使是用了pageheap,在某些情况下仍不能立即定位出错点.但大多数情况下还是很好用滴。
附:
那几个fd是debug模式下用来debug的,如前所说,操作系统级的排查只能查出在4k界上的溢出,但是在debug模式下,如果那4个fd在delete时发现被改过了,也会报错,也就是说,以下代码会在delete时,发现4个fd被改过而报错:
char *pCTest = new char[1];
pCTest[4] = 1;
delete []pCTest;
但是,下面这份由于是改动了d0,却一切正常:
char *pCTest = new char[1];
pCTest[5] = 1;
delete []pCTest;
那几个d0的出现应该是由于字节对齐的问题,我的环境下好像不论啥类型的都是以8字节对齐,囧...
如果是在release模式下,fd和d0都不会出现,也就不会有溢出检查了。所以下面的代码会在debug模式下报错,而release下正常:
char *pCTest = new char[1];
pCTest[4] = 1;
delete []pCTest;