1

我有一个 C 代码应用程序。我正在使用 MS-VS2005 构建。我有一个使用 malloc 动态分配的输出数据缓冲区。

对于某些测试用例,正在 malloc 的内存大小低于生成的实际输出大小(以字节为单位)。较大的输出被写入较小的缓冲区,导致缓冲区溢出。结果,测试运行崩溃,MSVS-2005 显示一个窗口“堆损坏......”

我知道这与一些动态内存分配有关,但我花了很长时间才真正找到根本原因,因为我不怀疑内存分配,因为我分配了足够大的输出所需的大小。但是一个特定的测试用例产生的输出比我计算的要多,因此导致了崩溃。

我的问题是:

1.) 我可以使用哪些工具来检测这种动态内存缓冲区溢出情况。它们还可以帮助检测任何缓冲区溢出情况(无论缓冲区/数组是否位于堆、堆栈、全局内存区域)?

2.) 内存泄漏工具(比如 Purify)或 lint、klocworks 等代码分析工具在特定情况下会有所帮助吗?我相信它们必须是运行时分析工具。

谢谢你。

-广告。

4

4 回答 4

1

我在《编写实体代码》一书中第一次遇到的一种解决方案是malloc()用诊断代码“包装” API。

首先,诊断malloc()安排为尾随标记分配额外的字节。例如,分配的内存后面的另外四个字节被保留并包含字符“FINE”。

稍后,当指针 frommalloc()传递到时,会调用free()相应的诊断版本。free()在调用标准实现free()和释放内存之前,验证尾随哨兵;它应该是未修改的。如果 sentinel 被修改,则块指针在从 diagnostic 返回后的某个时间点被滥用malloc()

使用内存保护保护页而不是哨兵模式来检测缓冲区溢出是有优势的。特别是,使用基于模式的方法,只有在事后才检测到非法内存访问。哨兵模式方法仅检测到非法写入。内存保护方法捕获非法读取和写入,并在它们发生时立即检测到。

的诊断包装函数malloc()还可以解决 的其他误用malloc(),例如free()对同一内存块的多次调用。此外,realloc()可以修改为在调试环境中执行时始终移动块,以测试realloc().

特别是,诊断包装器可以记录所有分配和释放的块,并在程序退出时报告内存泄漏。内存泄漏是在程序执行期间分配的未释放的块。

封装malloc()API 时,必须封装所有相关函数,包括calloc(), realloc(),strdup()

包装这些函数的典型方法是通过预处理器宏:

#define malloc(s) diagnostic_malloc(s, __FILE__, __LINE__)
/* ETC.... */

如果需要对标准实现的调用进行编码(例如,分配的块将被传递给第三方、仅二进制库,该库希望使用标准free()实现释放块,则可以访问原始函数名称而不是使用预处理器宏(malloc)(s)-- 即在函数名周围放置括号。

于 2009-12-28T22:26:42.767 回答
1

您可以尝试使用 VirtualAlloc 分配足够的页面 + 1,将 VirtualProtect 与 PAGE_READONLY | 一起使用 最后一页上的 PAGE_GUARD 标志,然后对齐可疑分配,使对象的结尾靠近受保护页的开头。如果一切顺利,您应该在访问保护页面时遇到访问冲突。如果您大致知道哪个分配被覆盖,这会有所帮助。否则,它需要覆盖所有可能需要大量额外内存的分配(每个分配至少 2 页)。我在此将其命名为“统计页面保护”的这种技术的一种变体是仅以这种方式为相对较小百分比的分配随机分配内存,以避免小对象的大膨胀。在大量的执行运行中,您应该能够遇到错误。在这种情况下,随机数生成器必须像时间一样被播种。同样,如果您怀疑在较低地址进行覆盖,您可以在对象前面分配保护页(不能同时进行这两种操作,但也可以随机混合)。

更新:原来 gflags.exe(以前是 pageheap.exe)微软实用程序已经支持“统计页面保护”,所以我重新发明了轮子 :) 你需要做的就是运行 gflags.exe /p /enable [/ random 0-100] YourApplication.exe 并运行您的应用程序。如果您在堆分配上使用自定义堆或自定义保护,那么您可以简单地切换到使用 HeapAlloc 至少来捕获错误,然后再切换回来。Gflags.exe 是支持工具包的一部分,可以从微软下载中心下载,在那里搜索即可。

于 2009-12-15T00:27:18.670 回答
0

PC-Lint 可以捕获某些形式的 malloc/new size 问题,但我不确定它是否会找到您的问题。

VS2005 在调试模式下(在函数结束时运行)对堆栈对象有很好的缓冲区溢出检查。它会定期检查堆。

至于它有助于追踪问题发生的位置,这就是我倾向于开始使用宏来转储所有分配以在以后匹配损坏的内存的地方(当它被检测到时)。

痛苦的过程,所以我也渴望学习更好的方法。

于 2009-11-19T18:37:02.733 回答
0

考虑我们的内存安全检查。我认为它会捕获您描述的所有错误。是的,它是对每次访问的运行时检查,有一些相当大的开销(不像我们认为的 valgrind 那样糟糕),其好处是可以诊断出第一个错误的程序操作。

于 2011-03-11T03:54:28.107 回答