Program의 수행에서 CPU의 연산을 위해 Stack을 사용한다는 점에서 Stack의 Size가 제한되어 있다는 것은 개발자라면 항상 염두에 두고 있을 것이다. 그러므로, 무한 재귀 호출되는 Function이나 과도하게 Stack 상에다 Allocation한 후(일반적으로 Local variables나 parameters는 Stack에 저장된다.) Exception이나 비정상적인 operation으로 인하여 Stack이 unwind 되지 않는 다면, 예기치 않은 Stack overflow를 맞을 수 있다. 다음의 Stack overflow dump case를 예로 든다.
일단, 메모리덤프를 Open 하면, 대번 Stack Overflow임을 알 수 있다.
(24f8.f64): Stack overflow - code c00000fd (first/second chance not available)
eax=000900a0 ebx=00000000 ecx=08ac2bf0 edx=0e010026 esi=0e010024 edi=08aff134
eip=033f9577 esp=08afebe8 ebp=08afec1c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** WARNING: Unable to verify checksum for ttt.dll
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ttt.dll -
ttt!DllUnregisterServer+0x33a4:
033f9577 8501 test dword ptr [ecx],eax ds:0023:08ac2bf0=00000000
Stack Size는 다음과 같이 Check 할 수 있다.
0:066> !teb
TEB at 7fefd000
ExceptionList: 08afec10
StackBase: 08b00000
StackLimit: 08ac1000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7fefd000
EnvironmentPointer: 00000000
ClientId: 000024f8 . 00000f64
RpcHandle: 00000000
Tls Storage: 7fefd02c
PEB Address: 7ffd8000
LastErrorValue: 0
LastStatusValue: c0150008
Count Owned Locks: 0
HardErrorMode: 0
0:066> ? 08b00000 - 08ac1000
Evaluate expression: 258048 = 0003f000
실제, Max Allocation할 수 있는 Stack Size를 확인해 보면, 대략 256k임을 확인할 수 있다. 이는 아래와 같은 TEB Structure의 DeallocationStack 값을 확인하면 되며, 이는 http://msdn.microsoft.com/en-us/library/cc267849.aspx 문서를 참조하거나 debugging tools for windows help에서 참조할 수 있다.
typedef struct _TEB {
NT_TIB NtTib;
PVOID EnvironmentPointer;
CLIENT_ID ClientId;
PVOID ActiveRpcHandle;
PVOID ThreadLocalStoragePointer;
PPEB ProcessEnvironmentBlock;
ULONG LastErrorValue;
.....
PVOID DeallocationStack;
.....
} TEB;
0:066> dd 7fefd000+e0c L1
7fefde0c 08ac0000
0:066> ? 08b00000 - 08ac0000
Evaluate expression: 262144 = 00040000 <---- 256k Max allocation size
그러므로, 현재 사용하는 Stack은 3f000 으로 Max Stack Size, 40000 에 도달하고 있음을 확인할 수 있다.
원인을 찾기 위해선 무엇보다도 먼저 재귀 호출이 있는 지 확인이 필요한데, 이는 Callstack을 확인하는 작업이 필요할 듯 하다. 일단, 해당 case에서는 안타깝게도 해당 모듈에 대한 Symbol이 정확하지 않아서 확인이 불가능했으며, 정확한 symbol이 존재한다면 아래의 Callstack에서 확인된 Function을 Check해야 한다.
0:066> kbL
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
08afec1c 033f4de9 0df4002c 08aff0fc 0000fde9 ttt!DllUnregisterServer+0x33a4 <-- Symbol이 맞지 않아서 이렇게 보이는 것임.
08aff118 033f40c8 0b056198 0b056228 00000000 ttt+0x4de9
08aff170 762e31eb 0b055f20 08aff124 08aff120 ttt+0x40c8
08aff198 7635184f 033f3e71 08aff3a0 00000005 rpcrt4!Invoke+0x2a
08aff5c4 7634fc79 09dae6f8 06afcbc8 018be228 rpcrt4!NdrStubCall2+0x27b
08aff614 760527f7 09dae6f8 018be228 06afcbc8 rpcrt4!CStdStubBuffer_Invoke+0xffffdd13
08aff638 76909759 09dae6f8 018be228 06afcbc8 oleaut32!CUnivStubWrapper::Invoke+0xc7
. . .
만일, Function의 무한 재귀호출에 의해서 Stack Overflow가 발생한 것이 아니라면, Stack에 allocation 패턴도 Check해야 한다.
0:066> dds esp
08afebe8 75f751ea kernel32!lstrlenW+0x56
08afebec 033f18c5 ttt+0x18c5
08afebf0 08aff134
08afebf4 0b0584a8
08afebf8 08aff148
08afebfc 01e62f00
08afec00 0b0584a8
08afec04 000cc09e
08afec08 08afec24
08afec0c 033f188b ttt+0x188b
08afec10 08aff10c
08afec14 0340c9f0 ttt!DllUnregisterServer+0x1681d
08afec18 00000000
08afec1c 08aff118
08afec20 033f4de9 ttt+0x4de9
08afec24 0df4002c
08afec28 08aff0fc
08afec2c 0000fde9
08afec30 0000007b
08afec34 0b055f20
08afec38 00000005
08afec3c 00000000
Callstack에서 문제가 발생한 시점이전의 최종 Return Address가 33f4de9 인데, 그 이후에 Stack address(상위 raw stack에서의 회색 블럭)는 local variables 또는 parameters 등으로 사용되므로, 이들 중 Allocation 패턴이 overflow를 의심하게 하는 것이 있는 지 Check가 필요하다.
0:066> dc 0b0584a8
0b0584a8 0340e448 0b0500c4 00000000 00000000 H.@.............
0b0584b8 ee7ff470 08002142 00000001 0000261c p...B!.......&..
0b0584c8 0000261c 000a000d 006f0043 0074006e .&......C.o.n.t.
0b0584d8 006e0065 002d0074 00790054 00650070 e.n.t.-.T.y.p.e.
0b0584e8 0020003a 006d0069 00670061 002f0065 :. .i.m.a.g.e./.
0b0584f8 00690067 003b0066 006e0020 006d0061 g.i.f.;. .n.a.m.
0b058508 003d0065 00630022 0069006c 005f0070 e.=.".c.l.i.p._.
0b058518 006d0069 00670061 00300065 00330030 i.m.a.g.e.0.0.3.
0:066> du 0b0584c8+8
0b0584d0 "Content-Type: image/gif; name="c"
0b058510 "lip_image003.gif"..Content-ID: <"
0b058550 "20090223.44575.001@tagfree.com>."
0b058590 ".Content-Transfer-Encoding: base"
0b0585d0 "64..Content-Location: file:///C:"
0b058610 "/DOCUME~1/jkim/LOCALS~1/Temp/mso"
0b058650 "html1/01/clip_image003.gif....R0"
0b058690 "lGODlhKwHTAHcAMSH+GlNvZnR3YXJlOi"
0b0586d0 "BNaWNyb3NvZnQgT2ZmaWNlACH5BAEAAA"
0b058710 "AALBUACQAA..AcMAhgAAAAAAABoaTSYm"
0b058750 "czMzmWaAgIAzAICAAJaWlpm/v5/P/57O"
0b058790 "/53O/5zN/5vN/5rM/5nM/79N..AL/d/7"
0b0587d0 "7d/7zc/7vb/7ra/7na/73c/7jZ/7fZ/7"
0b058810 "bY/7XY/7TY/7PX/7LX/7DW/6/W/7TX/6"
0b058850 "/V/67V../63U/6vT/6rT/6nT/6jT/6zU"
0b058890 "/6vU/6fS/6bS/6bR/6XR/6TQ/6PQ/6LQ"
0b0588d0 "/6HP/6DP/8Tf/8Le/8He../8De/8Pf/8"
0b058910 "z//9/u/97t/93s/9zr/9vr/9rq/9nq/9"
0b058950 "jq/9rr/9bp/9Xo/9Tn/9fp/9Lm/9Hm/8"
0b058990 "/l../9Pn/87l/83k/9Dm/8zj/8vj/8ni"
0b0589d0 "/8ji/8fh/8bh/8Xg//9mAP//AP//zP7/"
0b058a10 "//7+//3+//z9//v9..//v8//r8//n8//"
0b058a50 "j7//f7//b6//X5//T4//P4//L4//H3//"
. . .<중략>
그러므로, 만일, 상위에서 보여주는 몇몇의 data가 과도한 Allocation이나 Allocation 패턴에 의해서 문제 상황을 가져오지는 않는 지는 최종적으로 Source Code를 통해서 확인해봐야 하며, 그러기 위해서는 정확한 Symbol의 match가 중요함을 잊지 말아야 한다.



덧글