Stack Overflow 사례
저장소/잡다한거Stack Overflow가발생하는경우를본다.
(Demo: StackOverflow.exe 예제)
Unknown software exception (0xc00000fd)와같은exception이발생할수있는데이때windbg를연결한다
0:000> kL
ChildEBP RetAddr
0003bba0 00401091 StackOverflow!_chkstk+0x27
00054264 00401091 StackOverflow!StackOverflow+0x91
0006c928 00401091 StackOverflow!StackOverflow+0x91
00084fec 00401091 StackOverflow!StackOverflow+0x91
0009d6b0 00401091 StackOverflow!StackOverflow+0x91
000b5d74 00401091 StackOverflow!StackOverflow+0x91
000ce438 00401091 StackOverflow!StackOverflow+0x91
000e6afc 00401091 StackOverflow!StackOverflow+0x91
000ff1c0 00401091 StackOverflow!StackOverflow+0x91
00117884 00401091 StackOverflow!StackOverflow+0x91
0012ff48 004010fa StackOverflow!StackOverflow+0x91
0012ff54 00401803 StackOverflow!main+0xa
0012ffb8 004015cd StackOverflow!__tmainCRTStartup+0x233
0012ffc0 77e6f23b StackOverflow!mainCRTStartup+0xd
0012fff0 00000000 kernel32!BaseProcessStart+0x23
같은함수명이recursive하게반복되므로stack overflow라는것을쉽게짐작할수있다. 즉, stack frame이recursive 함수호출을통해grow하다보면stack size를초과했을것이다. 또한_chkstk가stack 문제임을나타낸다.
디폴트로thread의stack size는1MB이다. 현thread의stack base와limit은어떤가체크하려면thread environment block을본다
0:000> !teb
TEB at 7ffdd000
ExceptionList: 0012ffa8
StackBase: 00130000
StackLimit: 00031000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdd000
EnvironmentPointer: 00000000
ClientId: 000018ec . 00001f84
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffde000
LastErrorValue: 0
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
Frame간의크기를보려면kf를사용한다
0:000> kfL
Memory ChildEBP RetAddr
0003bba0 00401091 StackOverflow!_chkstk+0x27
186c4 00054264 00401091 StackOverflow!StackOverflow+0x91
186c4 0006c928 00401091 StackOverflow!StackOverflow+0x91
186c4 00084fec 00401091 StackOverflow!StackOverflow+0x91
186c4 0009d6b0 00401091 StackOverflow!StackOverflow+0x91
186c4 000b5d74 00401091 StackOverflow!StackOverflow+0x91
186c4 000ce438 00401091 StackOverflow!StackOverflow+0x91
186c4 000e6afc 00401091 StackOverflow!StackOverflow+0x91
186c4 000ff1c0 00401091 StackOverflow!StackOverflow+0x91
186c4 00117884 00401091 StackOverflow!StackOverflow+0x91
186c4 0012ff48 004010fa StackOverflow!StackOverflow+0x91
c 0012ff54 00401803 StackOverflow!main+0xa
64 0012ffb8 004015cd StackOverflow!__tmainCRTStartup+0x233
8 0012ffc0 77e6f23b StackOverflow!mainCRTStartup+0xd
30 0012fff0 00000000 kernel32!BaseProcessStart+0x23
StackOverflow!StackOverflow 한번호출시frame간distance는186c4, 십진수로100036(100K에못미치는값)이다. Local vars에그정도를쓰는것이다. 이를열번정도호출하면1MB에가깝다.
Stack overflow인지 확인방법은stack이현재어느만큼쓰고있고limit에도달했는지확인하는것이다.
이를계산하기전에, 일반적인 stack의구조를보자
+------------------------------------------------+ TEB 구조체의DeallocationStack이가리키는주소
BOTTOM/RSV
+------------------------------------------------+ < ---------- 근접하게되면Stack Overflow!!
………..
+------------------------------------------------+
RESERVE 페이지
+------------------------------------------------+
GUARD 페이지
+------------------------------------------------+ StackLimit
TOP/COMMIT 페이지
+------------------------------------------------+ StackBase
아래가큰주소이고위는작은주소의방향일때이다. 스택은큰주소에서작은주소방향으로커간다.
그래서그림상가장하단의주소가StackBase이고실행시처음COMMIT된페이지에서작은주소방향으로다음페이지를COMMIT하면서계속커나갈수있다. StackLimit은현재stack의경계선을나타낸다. 그러므로현재의stack 크기는StackBase에서StackLimit만큼의차이이다(StackBase쪽이더큰주소이므로StackBase에서StackLimit을뺀다)
Windows SDK의ntpsapi.h 파일에정의된TEB 구조체의DeallocationStack은Stack의maximum size일때의주소를가리킨다. 그러나stack이커나가더라도DeallocationStack까지도달할수있는것은아니고DeallocationStack의마지막페이지에근접하면stack overflow를발생하게된다.
위!teb에서보인StackBase와StackLimit로써현stack size를계산한다.
StackBase: 00130000
StackLimit: 00031000
0:000> ?130000-31000
Evaluate expression: 1044480 = 000ff000
거의1MB에달한다는것을알수있다.
Stack이도달할수있는maximum stack size는어떻게될까? StackBase에서DeallocationStack의차이를보면된다.
DeallocationStack은_TEB 구조체를분석하면e0x의offset을가진다. (계산방법생략)
0:000> dd 7ffdf000+e0c L1
7ffdfe0c 00030000
0:000> ?130000-00030000
Evaluate expression: 1048576 = 00100000
예상대로maximum stack size는1MB이다.
Maximum stack size의마지막페이지는31000에서30000이다.
0:000> r esp
esp=0003bb98
거의31000에근접하였으므로stack overflow가발생하였음을알수있다.
해결방법은잘못된recursive function 콜을바로잡거나(recursive function이영원히호출되지않도록끝나는조건지정)
Stack의디폴트사이즈를늘려준다. 더좋은것은메모리할당에있어서stack보다는heap 영역을쓰는것이다.
<><><>
Stack Overflow의디버깅방법은다음MSDN을참조하면좋다.
http://msdn.microsoft.com/en-us/library/cc267849.aspx
user-mode에서의Stack overflow가recursion 에의해주로발생하는reserved stack 영역을다쓴경우가아닌다른원인이있는예제를보여준다.
A stack overflow is an error that user-mode threads can encounter. There are three possible causes for this error:
- A thread uses the entire stack reserved for it. This is often caused by infinite recursion.
- A thread cannot extend the stack because the page file is maxed out, and therefore no additional pages can be committed to extend the stack.
- A thread cannot extend the stack because the system is within the brief period used to extend the page file.
'저장소 > 잡다한거' 카테고리의 다른 글
정규표현식(Regular Expressions) 문법 (0) | 2013.04.02 |
---|---|
TortoiseSVN Commands (0) | 2013.03.19 |
심볼서버(Symbol Server) (0) | 2013.02.07 |
국가 코드(Country Codes), 대륙 코드(Continent Codes), 언어 코드 (0) | 2013.01.09 |
Syntax Highlighter 적용 방법 (0) | 2012.11.09 |