【问题标题】:Meaning of "RtlpAnalyzeHeapFailure" in the watch-window of a dump转储监视窗口中“RtlpAnalyzeHeapFailure”的含义
【发布时间】:2018-04-05 07:43:56
【问题描述】:

我正在处理以下访问冲突:

Unhandled exception at 0x77DB2A10 (ntdll.dll) in <Process>.exe.dmp: 
0xC0000005: Access violation reading location 0x184487B8. occurred

源码如下:

Result CParameter::SetValue(..., ..., <internal_Class>* pBlock, ...)
{
  ...
  <internal_Class>* pStore = nullptr;
  if (!pBlock)
  {
    pStore = &m_Data; // m_Data is a global variable
  }
  else pStore = pBlock;

  if (pStore->pbData)
  {
    pStore->Clear(); // here we have the crash
  }

调用堆栈如下所示:

   ntdll.dll!_RtlpCoalesceFreeBlocks@16()  Unknown Non-user code. Symbols loaded.
   ntdll.dll!@RtlpFreeHeap@16()    Unknown Non-user code. Symbols loaded.
   ntdll.dll!_RtlFreeHeap@12() Unknown Non-user code. Symbols loaded.
   ole32.dll!CRetailMalloc_Free(IMalloc * pThis=0x777476bc, void * pv=0x1842de40) Line 687  C++ Non-user code. Symbols loaded.
   ole32.dll!CoTaskMemFree(void * pv=0x1842de40) Line 475   C++ Non-user code. Symbols loaded.
=> <Process>.exe!CParameter::SetValue(..., <internal_Class> * pBlock=0x00000000, ...) Line 5528 C++ Symbols loaded.

在监视窗口中,我看到 &m_Data 的以下值:

0x77e4f9ae {Inside ntdll.dll!_RtlpAnalyzeHeapFailure@12()} {pbData=0xd2b70f3d <Error reading characters of string.> ...}

更多信息:

  • pBlock 的值为NULL
  • 调试器不知道pStore 的值

问题:

  • 调用堆栈中的函数“Clear()”在哪里?
  • 监视窗口中的值“RtlpAnalyzeHeapFailure”怎么样?这是否意味着我的转储已损坏到无法从中获取任何有用信息?

提前致谢

【问题讨论】:

  • 看来问题出在pStore-&gt;Clear();
  • 是否有任何CoTaskMemAlloc/CoTaskMemFree 或任何使用这些分配的OLE 函数?
  • @BarmakShemirani:确实,我已经验证了代码,CoTaskMemAlloc 确实用于内存分配。
  • 这只是一个沼泽标准堆损坏问题。当堆管理器倒下时,你得到了一个快照,你没有一个关于堆损坏的确切时刻的快照。这发生在早些时候,可以通过任何代码完成。这使得此类错误非常难以诊断。对于您知道存在损坏问题的程序,最好的方法是通过应用程序验证程序来完成它的步伐。

标签: c++ windows visual-studio crash dump


【解决方案1】:

调用堆栈中的函数“Clear()”在哪里?

这看起来像是内联的。

调试器不知道 pStore 的值

不是还是来自m_Data吗?

如果你查看崩溃点附近的代码,你可以看到它最近在哪个寄存器中,如果它被保留,那么你应该能够看到它在某个时候被保存。

错误是由于内存系统检测到内存被错误地释放。这可能是 m_Data 持有一个浮动值,或者已经被删除了。

我已经制作了类似的功能。

__declspec(noinline) void Type::Clear()
{
    delete pbData;
    pbData = nullptr;
}

__declspec(noinline) void SetValue(Type * pBlock)
{
    Type * pStore = nullptr;
    if (!pBlock)
    {
        pStore = &m_Data; // m_Data is a global variable
    }
    else pStore = pBlock;

    if (pStore->pbData)
    {
        pStore->Clear(); // here we have the crash
    }
}

它的反汇编(来自windbg)是:-

0:000:x86> uf debugging2!SetValue
debugging2!Type::Clear [c:\source\example\debugging2\debugging2.cpp @ 17]:
   17 01041020 56              push    esi
   17 01041021 8bf1            mov     esi,ecx
   18 01041023 6a00            push    0
   18 01041025 ff36            push    dword ptr [esi]
   18 01041027 e85c000000      call    debugging2!operator delete (01041088)
   18 0104102c 83c408          add     esp,8
   19 0104102f c70600000000    mov     dword ptr [esi],0
   19 01041035 5e              pop     esi
   20 01041036 c3              ret

debugging2!SetValue [c:\source\example\debugging2\debugging2.cpp @ 23]:
   23 01041040 833df833040100  cmp     dword ptr [debugging2!m_Data (010433f8)],0
   31 01041047 740a            je      debugging2!SetValue+0x13 (01041053)  
Branch

debugging2!SetValue+0x9 [c:\source\example\debugging2\debugging2.cpp @ 33]:
   33 01041049 b9f8330401      mov     ecx,offset debugging2!m_Data (010433f8)
   33 0104104e e9cdffffff      jmp     debugging2!Type::Clear (01041020)  Branch

debugging2!SetValue+0x13 [c:\source\example\debugging2\debugging2.cpp @ 35]:
   35 01041053 c3              ret  Branch

这表明(在我的情况下)对 Clear() 的调用已被跳转取代,并通过优化将其隐藏在堆栈中。 这也表明地址01041049 ecx 加载了调用的值。 MSDN : x86 calling conventions Ecx 不是保留值,因此我们将无法找到它所持有的值(除了它似乎是来自您的 cmets 的 &m_Data)。

但我们可以查看堆栈中的函数...

在 ::Clear 函数中,将 ecx 移至 esi。所以 esi(被保留)现在具有相同的值。

看下一个函数(操作符删除),

debugging2!operator delete 
 [f:\dd\vctools\crt\vcstartup\src\heap\delete_scalar_size.cpp @ 14]:
   14 01041088 55              push    ebp
   14 01041089 8bec            mov     ebp,esp
   15 0104108b ff7508          push    dword ptr [ebp+8]
   15 0104108e e890030000      call    debugging2!operator delete (01041423)
   15 01041093 59              pop     ecx
   16 01041094 5d              pop     ebp
   16 01041095 c3              ret

我们看到 esi 没有改变,也没有保存。所以我们看下栈中的下一个函数......

0:000:x86> uf 01041423
Flow analysis was incomplete, some code may be missing
debugging2!operator delete 
[f:\dd\vctools\crt\vcstartup\src\heap\delete_scalar.cpp @ 15]:
   15 01041423 e982090000      jmp     debugging2!free (01041daa)  Branch

在每种情况下,我们都在寻找 esi 存储在堆栈中的某个位置,因此我们可以找到它...

【讨论】:

  • 对不起,但是这里我缺少一些背景信息:我怎么知道我最近在哪个寄存器(我在看转储,我不是在调试程序),即使我找到任何与寄存器相关的信息,您所说的保留寄存器是什么意思?供您参考,“注册”调试窗口显示以下信息:EAX = 00003530 EBX = 00000000 ECX = 77E4F846 EDX = 1FD29440 ESI = 184487B8 EDI = 00730000 EIP = 0014FA36 ESP = 1D7DF07C EBP = 1D7DF0D4 EFL = 00000000 0x184487B8 = 00000000
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-06
  • 1970-01-01
  • 2019-11-06
  • 2012-04-05
  • 1970-01-01
相关资源
最近更新 更多