如何诊断混合托管/非托管x32 .NET应用程序中的已损坏后缀模式

时间:2014-02-12 22:30:43

标签: c# pinvoke windbg unmanaged

我有一个.NET应用程序,它可以驱动几个库,全部是32位(应用程序也是32位)。我最近开始收到GC启动释放内存时发生的崩溃错误,当我附加时,我发现它是一个访问冲突。经过一些网络搜索后,我自己设置了gflags和windbg,并且能够解决实际问题:

===========================================================
VERIFIER STOP 0000000F: pid 0x9650: corrupted suffix pattern 

001B1000 : Heap handle
20A5F008 : Heap block
00000006 : Block size
20A5F00E : corruption address
===========================================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.
===========================================================

在做了一些阅读之后,我得到了一个堆栈跟踪:

0:009> !heap -p -a 20A5F008
address 20a5f008 found in
_HEAP @ f420000
  HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
    20a5efe0 0008 0000  [00]   20a5f008    00006 - (busy)
    Trace: 0a94
    60cba6a7 verifier!AVrfpDphNormalHeapAllocate+0x000000d7
    60cb8f6e verifier!AVrfDebugPageHeapAllocate+0x0000030e
    77e00d96 ntdll!RtlDebugAllocateHeap+0x00000030
    77dbaf0d ntdll!RtlpAllocateHeap+0x000000c4
    77d63cfe ntdll!RtlAllocateHeap+0x0000023a
    60cccb62 verifier!AVrfpRtlAllocateHeap+0x00000092
    7666ea43 ole32!CRetailMalloc_Alloc+0x00000016
    7666ea5f ole32!CoTaskMemAlloc+0x00000013
    6c40b25d clr!MngdNativeArrayMarshaler::ConvertSpaceToNative+0x000000bd

...以及关于块条目的一些更详细的信息:

0:009> !heap -i 20a5f008
Detailed information for block entry 20a5f008
Assumed heap       : 0x0f610000 (Use !heap -i NewHeapHandle to change)
Header content     : 0x00000000 0x00000001
Owning segment     : 0x0f610000 (offset 0)
Block flags        : 0x0 (free )
Total block size   : 0x0 units (0x0 bytes)
Previous block size: 0xb4e4 units (0x5a720 bytes)
Block CRC          : OK - 0x0  
List corrupted: (Blink->Flink = 00000000) != (Block = 20a5f010)
Free list entry    : CORRUPTED
Previous block     : 0x20a048e8
Next block         : 0x20a5f008

我对这些数据感到困惑。不幸的是,ConvertSpaceToNative并不是一个有启发性的调用,因为它包含了......几乎每个非托管分配请求。我已经尝试进一步扩展以找到我需要的信息,以追溯到违规的电话,并花了几天查看文档,但我没有找到一种方法来确定腐败的实际来源。我已经尝试设置断点并单步执行,但我找不到一种方法来手动验证堆的内容实际上有效 - 它总是报告一切正常。在我看来,我应该能够通过打开整页堆来立即停止应用程序,但它看起来仍然没有停止,直到空闲调用(这是执行暂停时的调用堆栈):

0:009> kL
ChildEBP RetAddr  
2354ecac 60cb9df2 verifier!VerifierStopMessage+0x1f8
2354ed10 60cba22a verifier!AVrfpDphReportCorruptedBlock+0x1c2
2354ed6c 60cba742 verifier!AVrfpDphCheckNormalHeapBlock+0x11a
2354ed8c 60cb90d3 verifier!AVrfpDphNormalHeapFree+0x22
2354edb0 77e01564 verifier!AVrfDebugPageHeapFree+0xe3
2354edf8 77dbac29 ntdll!RtlDebugFreeHeap+0x2f
2354eeec 77d634a2 ntdll!RtlpFreeHeap+0x5d
2354ef0c 60cccc4f ntdll!RtlFreeHeap+0x142
2354ef54 76676e6a verifier!AVrfpRtlFreeHeap+0x86
2354ef68 76676f54 ole32!CRetailMalloc_Free+0x1c
2354ef78 6c40b346 ole32!CoTaskMemFree+0x13
2354f008 231f7e8a clr!MngdNativeArrayMarshaler::ClearNative+0x78
WARNING: Frame IP not in any known module. Following frames may be wrong.
2354f08c 231f6442 0x231f7e8a
2354f154 231f5a7b 0x231f6442
2354f264 231f572b 0x231f5a7b
2354f288 231f56a4 0x231f572b
2354f2a4 231f7b3e 0x231f56a4
2354f330 231f207b 0x231f7b3e
2354f3b4 1edf60e0 0x231f207b
*** WARNING: Unable to verify checksum for     C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\045c9588954c3662d542b53f4462268b\mscorlib.ni.dll
2354f850 6a746ed4 0x1edf60e0
2354f85c 6a724157 mscorlib_ni+0x386ed4
2354f8c0 6a724096 mscorlib_ni+0x364157
2354f8d4 6a724051 mscorlib_ni+0x364096
2354f8f0 6a691cd2 mscorlib_ni+0x364051
2354f908 6c353e22 mscorlib_ni+0x2d1cd2
2354f914 6c363355 clr!CallDescrWorkerInternal+0x34
2354f968 6c366d1f clr!CallDescrWorkerWithHandler+0x6b
2354f9e0 6c4d29d6 clr!MethodDescCallSite::CallTargetWorker+0x152
2354fb54 6c3c8357 clr!ThreadNative::KickOffThread_Worker+0x19d
2354fb68 6c3c83c5 clr!Thread::DoExtraWorkForFinalizer+0x1ca
2354fc10 6c3c8492 clr!Thread::DoExtraWorkForFinalizer+0x256
2354fc6c 6c3c84ff clr!Thread::DoExtraWorkForFinalizer+0x615
2354fc90 6c4d2ad8 clr!Thread::DoExtraWorkForFinalizer+0x6b2
2354fd14 6c3fb4ad clr!ThreadNative::KickOffThread+0x1d2
2354feb0 60cd11d3 clr!Thread::intermediateThreadProc+0x4d
2354fee8 75c6336a verifier!AVrfpStandardThreadFunction+0x2f
2354fef4 77d69f72 KERNEL32!BaseThreadInitThunk+0xe
2354ff34 77d69f45 ntdll!__RtlUserThreadStart+0x70
2354ff4c 00000000 ntdll!_RtlUserThreadStart+0x1b

我觉得所谓的显然是我现在应该做的事情,但没有任何调查途径可以让我解决这个问题。

1 个答案:

答案 0 :(得分:1)

我终于解决了这个问题,发现我犯了一个关键错误。如果后缀模式被破坏,则错误将在免费尝试中出现,这使我相信分配在免费之前不太可能出现。这不准确。 在处理免费发生的损坏时,除非有更多信息,否则任何分配点都是可能的。在这种情况下,验证者停止释放一个参数,该参数被错误地定义为短路结构而不是作为整数的结构。

以下是有问题的代码:

    [DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetCharABCWidths(IntPtr hdc, uint uFirstChar, uint uLastChar, [Out] ABC[] lpabc);

(这个声明没问题)

    [StructLayout(LayoutKind.Sequential)]
    public struct ABC
    {
        public short A;
        public ushort B;
        public short C;
    }

(根据ABC结构的MSDN文章,这不可行:http://msdn.microsoft.com/en-us/library/windows/desktop/dd162454(v=vs.85).aspx

因此,如果您发现自己正在调试内存损坏而无法自由停止,请记住:永远不要忽视被释放的内存被错误地分配开始的可能性......并且关注非托管调用的那些[Out]参数!