什么是Heap Corruption
堆破坏是内存破坏的一种,简单来说就是由于程序的错误导致堆内存被意外改写,典型的情况包括:
- 申请了N个Bytes的内存,却试图写入> N个Bytes的内容;
- 向已经释放的内存位置写入内容;
fFrameSize = pSearch01To - fpBuffer; // 在某种情况下会出现fFrameSize = 0xFFFFFFFC
memcpy(fTo, fpBuffer, fFrameSize); // 这里memcpy会发生什么?
Heap Corruption的危害
把不属于自己的内存块的内容改写掉,危害是很显然的。更要命的是,Heap Corruption的问题往往很难排查,因为:
- 每次运行表现出来的现象可能不一样;
- Debug版和Release版表现可能不一样;
- 问题暴露的地方和问题引入的地方可能相差很远,两者逻辑上没有必然联系;
PageHeap检测Heap Corruption的原理
PageHeap工具可以在运行时检测Heap Corruption,其基本原理是在我们分配的内存块前后加上一些额外信息,这些额外信息用以检测是否出现越界写的情况。PageHeap分为Full模式和Normal模式:
- Full模式:在我们分配的每个内存块尾上加一页non-accessible page。Full模式的好处是“Sudden Death”,即在问题发生的点上立马Access Violation,易于Debugging。坏处是大量消耗内存。
- Normal模式:在我们分配的每个内存块前后加少量的填充字段。Normal模式的好处是内存消耗较少。坏处是只有在释放内存时才会检测出Heap Corruption,Debugging难度还是比较大。
PageHeap的使用方法
大家在WinDbg的安装路径下,可以看到gflags.exe这个工具,gflags集成了pageheap的功能。Gflags可以以GUI方式或Console方式运行,但是GUI方式只能启用PageHeap的Full模式,所以建议大家通过Console方式来使用gflags(以Run As Administrator方式打开Cmd窗口运行gflags)。
几条基本命令如下:
Gflags.exe /p /enable dest.exe
Gflags.exe /p /disable dest.exe
Gflags.exe /p
具体使用方法可以在WinDbg的帮助文档中搜索PageHeap,或在命令行模式下gflags /p /?
其他相关工具
除了gflags\pageheap之外,Application Verifier也可以用来检测Heap Corruption问题。Application Verifier也包含了其他很多运行时检查项,大家可以在工作中按需使用。Application Verifier的问题是它不支持托管代码。
写程序过程中预防Heap Corruption
PageHeap、Application Verifier或其他的运行时检查工具其实都是事后诸葛亮;对我们来说更重要的是在每天写程序的过程中就控制住,不给Heap Corruption机会。控制方法不止一种,就我们目前的情况来说,有两点是可以立即做的:
- Klocwork静态代码检查:Klocwork不能帮我们查出所有的Heap Corruption,但实践证明还是能查出一部分的;
- 使用安全的函数:就是xxx_s,如memcpy_s等;对于我们自己写的涉及内存操作的函数,也应设计成安全的。