정리

Stack Overflow 사례

저장소/잡다한거
원문 출처 : http://callibri.egloos.com/2801476


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 framerecursive 함수호출을통해grow하다보면stack size초과했을것이다또한_chkstkstack 문제임을나타낸다.

디폴트로threadstack size1MB이다threadstack baselimit어떤가체크하려면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 한번호출시framedistance186c4, 십진수로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 SDKntpsapi.h 파일에정의된TEB 구조체의DeallocationStackStackmaximum size때의주소를가리킨다그러나stack커나가더라도DeallocationStack까지도달할있는것은아니고DeallocationStack마지막페이지에근접하면stack overflow발생하게된다.

 

!teb에서보인StackBaseStackLimit로써stack size계산한다.

    StackBase:            00130000

    StackLimit:           00031000

 

0:000> ?130000-31000

Evaluate expression: 1044480 = 000ff000

거의1MB달한다는것을있다.

 

Stack도달할있는maximum stack size어떻게될까? StackBase에서DeallocationStack차이를보면된다.

DeallocationStack_TEB 구조체를분석하면e0xoffset가진다. (계산방법생략)

 

0:000> dd 7ffdf000+e0c L1

7ffdfe0c  00030000

 

0:000> ?130000-00030000

Evaluate expression: 1048576 = 00100000

예상대로maximum stack size1MB이다.

 

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 overflowrecursion 의해주로발생하는reserved stack 영역을경우가아닌다른원인이있는예제를보여준다.

 

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.