WinDbg 解決Font.ToLogFont AccessViolationExcetion

四六成雙發表於2013-08-08

有個程式總是在windows 2003 server 異常退出. 並且,  檢視呼叫棧也肯奇怪, 應該是很正常的呼叫. 懷疑是堆溢位.

開啟heap trace :

C:\Program Files\Debugging Tools for Windows (x86)>gflags -i app.exe +ust +hpa

發現在Font.ToLogFont函式遇到: {"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."} 

如下圖:

 

用Windbg開啟exe後發現:

(12e8.13e4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=07362e9c ebx=00000001 ecx=00000003 edx=4dd77680 esi=07362f4c edi=09b03000
eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
gdiplus!GpFont::GetLogFontW+0x12d:
4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

彙編程式碼:

4ddccc11 8b7004          mov     esi,dword ptr [eax+4]
4ddccc14 6a10            push    10h
4ddccc16 03f0            add     esi,eax
4ddccc18 83c71c          add     edi,1Ch
4ddccc1b 59              pop     ecx
4ddccc1c f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

 

重新執行程式,在4ddccc1b處打斷點, 

0:007> bp 4ddccc1b
0:007> g
Breakpoint 0 hit
eax=07362e9c ebx=00000001 ecx=5d5be675 edx=4dd77680 esi=07362f18 edi=080b2fdc
eip=4ddccc1b esp=0012e890 ebp=0012e8e8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
gdiplus!GpFont::GetLogFontW+0x12c:
4ddccc1b 59              pop     ecx

檢視edi所在堆還有多少可寫空間,

0:000> !heap -p -a edi
    address 080b2fdc found in
    _DPH_HEAP_ROOT @ 151000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 9bf6380:          80b2fc0               3c -          80b2000             2000
    7c83d7f0 ntdll!RtlAllocateHeap+0x00000e9f
    776bcfde ole32!CRetailMalloc_Alloc+0x00000016
    776bcf4b ole32!CoTaskMemAlloc+0x00000013
    79ac4920 mscorlib_ni+0x00244920
    79aca07b mscorlib_ni+0x0024a07b
    79ab516a mscorlib_ni+0x0023516a
    7b1effc4 System_Drawing_ni+0x0004ffc4
    7b1e46c0 System_Drawing_ni+0x000446c0
    7b1e452d System_Drawing_ni+0x0004452d

 
0:000> ? 80b2fc0+ 0x3c - edi
Evaluate expression: 32 = 00000020

 

剩餘32位元組.

而ecx指示需要寫入0x10 * 4=0x40位元組:

0:000> p
eax=07362e9c ebx=00000001 ecx=00000010 edx=4dd77680 esi=07362f18 edi=080b2fdc
eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
gdiplus!GpFont::GetLogFontW+0x12d:
4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

顯然不夠用, 執行後丟擲AV:

(6d8.1310): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=07362e9c ebx=00000001 ecx=00000007 edx=4dd77680 esi=07362f3c edi=080b3000
eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
gdiplus!GpFont::GetLogFontW+0x12d:
4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> ?(3c - 20)/4
Evaluate expression: 7 = 00000007

ecx 還剩7個DWORD寫不下.

原因是什麼??

LOGFONT在C#中定義是:

 [StructLayout(LayoutKind.Sequential)]
        private class LOGFONT
        {
            public int lfHeight = 0;
            public int lfWidth = 0;
            public int lfEscapement = 0;
            public int lfOrientation = 0;
            public int lfWeight = 0;
            public byte lfItalic = 0;
            public byte lfUnderline = 0;
            public byte lfStrikeOut = 0;
            public byte lfCharSet = 0;
            public byte lfOutPrecision = 0;
            public byte lfClipPrecision = 0;
            public byte lfQuality = 0;
            public byte lfPitchAndFamily = 0;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32) ] 
            public string lfFaceName = "";
        }

 

WinGDI.h定義:

/* Logical Font */
#define LF_FACESIZE         32

typedef struct tagLOGFONTA
{
    LONG      lfHeight;
    LONG      lfWidth;
    LONG      lfEscapement;
    LONG      lfOrientation;
    LONG      lfWeight;
    BYTE      lfItalic;
    BYTE      lfUnderline;
    BYTE      lfStrikeOut;
    BYTE      lfCharSet;
    BYTE      lfOutPrecision;
    BYTE      lfClipPrecision;
    BYTE      lfQuality;
    BYTE      lfPitchAndFamily;
    CHAR      lfFaceName[LF_FACESIZE];
} LOGFONTA, *PLOGFONTA, NEAR *NPLOGFONTA, FAR *LPLOGFONTA;

看上去沒有任何問題啊!!

但是 gdiplus並沒有按照CHAR來處理傳入進去的字串, 相反, 是按照WCHAR來處理的. 

WinGDI.h對於寬字元的LOGFONT定義是:

typedef struct tagLOGFONTW
{
    LONG      lfHeight;
    LONG      lfWidth;
    LONG      lfEscapement;
    LONG      lfOrientation;
    LONG      lfWeight;
    BYTE      lfItalic;
    BYTE      lfUnderline;
    BYTE      lfStrikeOut;
    BYTE      lfCharSet;
    BYTE      lfOutPrecision;
    BYTE      lfClipPrecision;
    BYTE      lfQuality;
    BYTE      lfPitchAndFamily;
    WCHAR     lfFaceName[LF_FACESIZE];
} LOGFONTW, *PLOGFONTW, NEAR *NPLOGFONTW, FAR *LPLOGFONTW;

因此是C#中的定義lfFaceName長度只有一半!

解決方法就是C#中定義 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

相關文章