我有一个C代码应用。我正在使用MS-VS2005构建。我有一个输出数据缓冲区,它是使用malloc动态分配的。
对于某些测试用例,正在malloc的内存大小比生成的实际输出大小(以字节为单位)短。较大的输出被写入较小的缓冲区,导致缓冲区溢出。因此,测试运行崩溃,MSVS-2005出现了一个窗口“堆损坏....”
我知道它与一些动态内存分配有关,但我花了很长时间才真正找到根本原因,因为我不怀疑内存分配,因为我分配了足够大的输出所需的大小。但是,一个特定的测试案例产生的输出比我计算的更多,因此导致了崩溃。
我的问题是:
1。)我可以使用哪些工具来检测这种动态内存缓冲区溢出情况。它们是否也可以帮助检测任何缓冲区溢出情况(无论缓冲区/数组是否在堆,堆栈,全局内存区域)?
2。)内存泄漏工具(比如说Purify)或代码分析工具如lint,klocworks会在特定情况下有所帮助吗?我相信他们必须是运行时分析工具。
谢谢。
-AD。
答案 0 :(得分:1)
您可以尝试使用VirtualAlloc分配足够的页面+ 1,使用带有PAGE_READONLY的VirtualProtect |最后一页上的PAGE_GUARD标志,然后对齐可疑分配,以便对象的末尾靠近受保护页面的开头。如果一切顺利,您应该在访问防护页面时获得访问冲突。如果您知道哪些分配被覆盖,这会有所帮助。否则,它需要覆盖所有可能需要大量额外内存的分配(每个分配至少2页)。我在此称为“统计页面保护”的这种技术的变体是仅以这种方式为相对较小百分比的分配随机分配内存,以避免小对象的大量膨胀。在大量执行运行中,您应该能够遇到错误。在这种情况下,随机数生成器必须像时间一样播种。类似地,如果您怀疑在较低地址处覆盖,则可以在对象前面分配保护页面(不能同时执行这两个操作,但也可以随机混合)。
更新:原来gflags.exe(曾经是pageheap.exe)微软实用程序已经支持“统计页面保护”所以我重新发明了轮子:)所有你需要做的就是运行gflags.exe / p /启用[/ random 0-100] YourApplication.exe并运行您的应用程序。如果您在堆分配上使用自定义堆或自定义防护,那么您可以简单地切换到使用HeapAlloc至少用于捕获错误然后切换回来。 Gflags.exe是支持工具包的一部分,可以从微软下载中心下载,只需在那里搜索。
答案 1 :(得分:1)
我在本书Writing Solid Code中首次遇到的一个解决方案是使用诊断代码“包装”malloc()
API。
首先,诊断malloc()
安排为尾随哨兵分配额外的字节。例如,保留分配的内存后的另外四个字节,并包含字符'FINE'。
稍后,当malloc()
的指针传递给free()
时,会调用free()
的相应诊断版本。在调用free()
的标准实现并放弃内存之前,验证尾随的哨兵;它应该是未经修改的。如果修改了标记,则在从诊断malloc()
返回之后的某个时刻,块指针被滥用。
使用内存保护保护页而不是标记模式来检测缓冲区溢出是有好处的。特别是,对于基于模式的方法,仅在事实之后才检测到非法存储器访问。 Sentinel模式方法仅检测到非法写入。内存保护方法捕获非法读写,并在发生时立即检测到它们。
malloc()
的诊断包装函数还可以解决malloc()
的其他误用,例如对同一内存块多次调用free()
。此外,realloc()
可以修改为在调试环境中执行时始终移动块,以测试realloc()
的调用者。
特别是,诊断包装器可以记录分配和释放的所有块,并在程序退出时报告内存泄漏。内存泄漏是在程序执行期间未释放分配的块。
在包装malloc()
API时,必须包含所有相关功能,包括calloc(), realloc(),
和strdup()
。
包装这些函数的典型方法是通过预处理器宏:
#define malloc(s) diagnostic_malloc(s, __FILE__, __LINE__) /* etc.... */
如果需要编写对标准实现的调用(例如,已分配的块将传递给第三方,仅二进制库,该库期望使用标准free()
实现释放块,可以使用(malloc)(s)
访问原始函数名而不是预处理器宏 - 也就是说,在函数名称周围放置括号。
答案 2 :(得分:0)
PC-Lint可以捕获某些形式的malloc / new size问题,但我不确定它是否会找到你的。
VS2005在调试模式下对堆栈对象进行了良好的缓冲区溢出检查(在函数结束时运行)。它定期检查堆。
至于它有助于追踪问题发生的位置,这是我倾向于开始使用宏来转储所有分配以便在以后(当检测到它时)与损坏的内存匹配。
痛苦的过程,所以我也渴望学习更好的方法。
答案 3 :(得分:0)
考虑我们的Memory Safety Check。我认为它会抓住你描述的所有错误。是的,它是每个访问的运行时检查,有一些相当大的开销(没有我们想象的那样糟糕),有利于诊断出错误的第一个程序操作。