如何在Windows上调查堆损坏?

rjzwgtxy  于 2023-05-08  发布在  Windows
关注(0)|答案(1)|浏览(158)

我观察到一个桌面崩溃。经过调试,结果发现这是由于无意中销毁了一个对象,但我想知道为什么这会导致堆损坏。以及为什么这不会触发我们的崩溃处理程序。
有一个QDialog A,它应该在应用程序的整个生命周期中保持活动。它使用new分配,并由std::shared_ptr管理。在某些情况下,此对话框被赋予另一个对话框B作为父对话框。另一个对话框超出了范围,并销毁了它的所有子对话框,包括对话框A。堆栈跟踪为:

ntdll.dll!RtlReportCriticalFailure()    
    ntdll.dll!RtlpHeapHandleError() 
    ntdll.dll!RtlpHpHeapHandleError()   
    ntdll.dll!RtlpLogHeapFailure()  
    ntdll.dll!RtlpFreeHeapInternal()    
    ntdll.dll!RtlFreeHeap() 
    ucrtbase.dll!_free_base()   
        A::`scalar deleting destructor'(unsigned int)
    QtCore4.dll!00000000573ba2cf()  
    QtGui4.dll!000000005696be43()   
        functionWhereBGoesOutOfScope()
        ...

为什么Windows在这个时间点已经抱怨了?我本来希望dtor在这里能顺利通过,但当应用程序关闭并且std::unique_ptr试图(再次)销毁A时,我希望会发生访问冲突。

异常处理?

为什么这不会触发我们的崩溃处理?我们调用SetUnhandledExceptionFilter来注册一个写崩溃转储的回调。但这根本不会发生。只是电脑死机了。如果没有附加调试器,进程将消失得无影无踪。

->在评论中回答:Heap corruption is special,不会触发任何异常处理程序。

我一直在努力

阅读文档。RtlFreeHeap documentation是无用的。错误情况或堆损坏根本没有得到解决。其他功能根本没有公共文档。
在阅读https://stackoverflow.com/a/22074401/872616后附加WinDbg。!heap

**************************************************************
*                                                            *
*                  HEAP ERROR DETECTED                       *
*                                                            *
**************************************************************

Details:

Heap address:  00000182adfa0000
Error address: 00000182ba16f050
Error type: HEAP_FAILURE_BLOCK_NOT_BUSY
Details:    The caller performed an operation (such as a free
            or a size check) that is illegal on a free block.
Follow-up:  Check the error's stack trace to find the culprit.

Stack trace:
Stack trace at 0x00007fff8ef39848
    00007fff8eede361: ntdll!RtlpLogHeapFailure+0x45
    00007fff8edf5bf0: ntdll!RtlpFreeHeapInternal+0x4e0
    00007fff8edf47b1: ntdll!RtlFreeHeap+0x51
    00007fff8c81f05b: ucrtbase!_free_base+0x1b
*** WARNING: Unable to verify checksum for xxx64.dll
    00007ffe8aacc227: xxx64!A::`scalar deleting destructor'+0x87
*** WARNING: Unable to verify checksum for ...\Qt-4.8.7-VS15x64\bin\QtCore4.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ...\Qt-4.8.7-VS15x64\bin\QtCore4.dll - 
    00000000573ba2cf: QtCore4!QObjectPrivate::deleteChildren+0x9f
*** WARNING: Unable to verify checksum for ...\Qt-4.8.7-VS15x64\bin\QtGui4.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ...\Qt-4.8.7-VS15x64\bin\QtGui4.dll - 
    000000005696be43: QtGui4!QWidget::~QWidget+0x893
    00007ffe8b1a8425: xxx64!functionWhereBGoesOutOfScope+0x345

上面写着“在空闲区块上非法”,意味着双重免费。这就解释了为什么Windows会抱怨。但据我所知,那个街区不应该是免费的。
我尝试在A的dtor中设置断点,但它们在Release模式下不会被触发,可能都是内联的。在Debug模式下,它们会被触发,但只有一次,这将排除双重释放。

yyyllmsg

yyyllmsg1#

原因是std::make_shared,可能是因为它使用了带有指针魔法的placement new。
作为一个实验,当我用std::shared_ptr::reset设置shared_ptr变量并直接调用new时,它的行为符合预期:问题发生在以后。因为shared_ptr被访问,或者当应用程序退出并且shared_ptr第二次尝试删除对话框时。
正确的解决方法是不使用shared_ptrQObject s。

相关问题