用于隔离堆栈粉碎错误的工具

Tools for Isolating a Stack smashing bug

本文关键字:错误 工具 隔离 堆栈 用于      更新时间:2023-10-16

说得委婉一点,我有一个小内存问题,并且没有工具和想法来隔离原因。

我有一个高度多线程(pthreads)的C/C++程序,在4.4.4之后和4.7.1之前使用GCC进行优化编译时,该程序开发了一个堆栈粉碎问题。

症状是,在创建其中一个线程的过程中,我得到了一个完整的堆栈崩溃,不仅是%RIP,而且所有的父帧和大多数寄存器都是0x00或其他非感测地址。导致该问题的线程似乎是随机的,但从日志消息来看,它似乎与同一个Hunk代码隔离,并且在创建新线程时似乎处于半可重复点。

这使得捕获和隔离有问题的代码比将其限制在一个几千行的编译单元更为困难,因为到目前为止,有问题的文件中的print()在试图缩小活动部分时是不可靠的。

导致线程最终破坏堆栈的线程创建是:


extern "C"
{
static ThreadReturnVal ThreadAPI WriterThread(void *act)
{
Recorder       *rec = reinterpret_cast  (act);
xuint64        writebytes;
LoggerHandle m_logger = XXGetLogger("WriterThread");
if (SetThreadAffinity(rec->m_cpu_mask))
{ ... }
SetThreadPrio((xint32)rec->m_thread_priority);
while (true)
{
... poll a ring buffer ... Hard Spin 100% use on a single core, this is that sort of crazy code. 
}
}

我尝试过调试版本,但症状只出现在优化版本-O2或更好的版本中。我试过Valgrind/memcheck和DRD,但在堆栈被吹走之前都没有发现任何问题(大约需要12小时才能达到故障)

使用-O2-Wstack保护程序编译不会发现任何错误,然而,一个带有-fstack保护程序的构建确实可以保护我免受该错误的影响,但不会发出任何错误。

电栅栏也有陷阱,但只有在烟囱消失之后。

问题:还有哪些工具或技术可以帮助缩小违规部分的范围?

非常感谢,--票据

解决这类问题的几个选项:

您可以尝试在损坏发生之前在堆栈地址上设置硬件断点,并希望调试器在损坏中尽早中断,以提供一个模糊有用的调试状态。这里棘手的部分是选择正确的堆栈地址;根据违规线程的"选择"的随机性,这可能并不实用。但从你的一条评论中,听起来往往是新创建的线程被破坏了,所以这可能是可行的。尝试在线程创建期间中断,获取线程的堆栈位置,偏移一些粗略的猜测,设置硬件BP,然后继续。根据你是否过早、太迟或根本没有休息,调整你的偏移量,冲洗,然后重复。这基本上是高级的猜测和检查,如果损坏模式过于随机,可能会受到严重阻碍或完全不可行,但令人惊讶的是,这经常会导致半可读的堆栈和成功的调试工作。

另一种选择是开始收集崩溃转储。试着在崩溃转储之间寻找可能有助于您更接近损坏源的模式。也许你会很幸运,其中一个崩溃转储会"更快"/"更快地到达源"。

不幸的是,这两种技术都是艺术而非科学;它们是不确定的,依赖于健康的运气,等等(至少根据我的经验……也就是说,有些人可以在崩溃时做出惊人的事情,但达到这种水平需要很多时间)。

还有一点需要注意:正如其他人所指出的,未初始化的内存是调试与发布差异的典型来源,很容易成为您的问题。然而,需要记住的另一种可能性是时间差异。线程的调度顺序和调度时间在调试和发布中通常有很大不同,并且很容易导致同步错误在其中一个中被掩盖,而在另一个中则没有。这些差异可能只是由于执行速度的差异,但我认为有些运行时故意在调试环境中扰乱线程调度。

您可以使用静态分析工具来检查一些可缝合的错误,也许其中一个发现的错误会导致您的错误。你可以在这里找到关于这些工具的一些信息。

相关文章: