Crash Dump Analysis Patterns (Part 9b)

Crash Dump Analysis Patterns (Part 9b)

원문링크: https://www.dumpanalysis.org/blog/index.php/2007/07/03/crash-dump-analysis-patterns-part-9b/

번역: 김희준 ( Heejune Kim, drost@naver.com, https://insidekernel.net, 2007-09-01 )

 

지난번 데드락 패턴에 이어지는 내용입니다. 이번 파트에서는 윈도우 커널에서의 ERESOURCE 데드락에 대해서 보여드리겠습니다.

ERESOURCE (executive resource)는 소유권 개념(ownership semantics)을 가지고 있는 윈도우 동기화 객체입니다.

Executive resource는 배타(exclusive)적으로 소유권을 갖도록 하거나, 소유권을 공유하도록 할 수도 있습니다. 즉 파일 공유와 유사합니다: 파일이 쓰기 권한으로 오픈되었다면 다른 객체들은 읽거나 쓸 수 없습니다; 만약 읽기 권한으로 파일을 오픈했다면 다른 객체들은 그것을 읽을 수는 있지만 쓰기는 할 수 없습니다.

ERESOURCE 구조체는 리스트로 링크되어져 있으며 스레드를 소유자로 가집니다. 이 스레드를 통해 우리가 커널 덤프나 전체 메모리 덤프에서 !locks 명령어를 사용했을 때 데드락을 빨리 찾아낼 수 있게 해 줍니다. 다음은 x86와 x64 윈도우에서의 _ERESOURCE의 정의입니다:

0: kd> dt -r1 _ERESOURCE
   +0x000 SystemResourcesList : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x008 OwnerTable       : Ptr32 _OWNER_ENTRY
      +0x000 OwnerThread      : Uint4B
      +0x004 OwnerCount       : Int4B
      +0x004 TableSize        : Uint4B
   +0x00c ActiveCount      : Int2B
   +0x00e Flag             : Uint2B
   +0x010 SharedWaiters    : Ptr32 _KSEMAPHORE
      +0x000 Header           : _DISPATCHER_HEADER
      +0x010 Limit            : Int4B
   +0x014 ExclusiveWaiters : Ptr32 _KEVENT
      +0x000 Header           : _DISPATCHER_HEADER
   +0x018 OwnerThreads     : [2] _OWNER_ENTRY
      +0x000 OwnerThread      : Uint4B
      +0x004 OwnerCount       : Int4B
      +0x004 TableSize        : Uint4B
   +0x028 ContentionCount  : Uint4B
   +0x02c NumberOfSharedWaiters : Uint2B
   +0x02e NumberOfExclusiveWaiters : Uint2B
   +0x030 Address          : Ptr32 Void
   +0x030 CreatorBackTraceIndex : Uint4B
   +0x034 SpinLock         : Uint4B

0: kd> dt -r1  _ERESOURCE
nt!_ERESOURCE
   +0x000 SystemResourcesList : _LIST_ENTRY
      +0x000 Flink            : Ptr64 _LIST_ENTRY
      +0x008 Blink            : Ptr64 _LIST_ENTRY
   +0x010 OwnerTable       : Ptr64 _OWNER_ENTRY
      +0x000 OwnerThread      : Uint8B
      +0x008 OwnerCount       : Int4B
      +0x008 TableSize        : Uint4B
   +0x018 ActiveCount      : Int2B
   +0x01a Flag             : Uint2B
   +0x020 SharedWaiters    : Ptr64 _KSEMAPHORE
      +0x000 Header           : _DISPATCHER_HEADER
      +0x018 Limit            : Int4B
   +0x028 ExclusiveWaiters : Ptr64 _KEVENT
      +0x000 Header           : _DISPATCHER_HEADER
   +0x030 OwnerThreads     : [2] _OWNER_ENTRY
      +0x000 OwnerThread      : Uint8B
      +0x008 OwnerCount       : Int4B
      +0x008 TableSize        : Uint4B
   +0x050 ContentionCount  : Uint4B
   +0x054 NumberOfSharedWaiters : Uint2B
   +0x056 NumberOfExclusiveWaiters : Uint2B
   +0x058 Address          : Ptr64 Void
   +0x058 CreatorBackTraceIndex : Uint8B
   +0x060 SpinLock         : Uint8B

 

만약 !locks 명령을 통해서 리소스들의 리스트를 확인할 수 있다면 그 리소스들을 소유하고 있는 스레드를 살펴볼 수 있습니다. 소유자 스레드는 스타 캐릭터(*)로 표시되어 있습니다:

0: kd> !locks
**** DUMP OF ALL RESOURCE OBJECTS ****
KD: Scanning for held locks......

Resource @ 0x8815b928    Exclusively owned
    Contention Count = 6234751
    NumberOfExclusiveWaiters = 53
     Threads: 89ab8db0-01<*>
     Threads Waiting On Exclusive Access:
        8810fa08       880f5b40       88831020       87e33020
        880353f0       88115020       88131678       880f5db0
        89295420       88255378       880f8b40       8940d020
        880f58d0       893ee500       880edac8       880f8db0
        89172938       879b3020       88091510       88038020
        880407b8       88051020       89511db0       8921f020
        880e9db0       87c33020       88064cc0       88044730
        8803f020       87a2a020       89529380       8802d330
        89a53020       89231b28       880285b8       88106b90
        8803cbc8       88aa3020       88093400       8809aab0
        880ea540       87d46948       88036020       8806e198
        8802d020       88038b40       8826b020       88231020
        890a2020       8807f5d0

 

여기에서 우리는 _KTHREAD 89ab8db0가 _ERESOURCE 8815b928 릴리즈 하기를 53개의 스레드가 기다리고 있는 것을 확인할 수 있습니다. 이 스레드 주소를 검색해보면 아래 내용을 알 수 있습니다:

Resource @ 0x88159560    Exclusively owned
    Contention Count = 166896
    NumberOfExclusiveWaiters = 1
     Threads: 8802a790-01<*>
     Threads Waiting On Exclusive Access:
                    89ab8db0

 

우리는 스레드 89ab8db0가 스레드 8802a790이 리소스 88159560을 릴리즈 하기를 기다리고 있는 것을 확인할 수 있습니다. 우리는 스레드 8802a790이 다른 스레드를 기다리는 경우를 계속해서 찾아보았습니다:

Resource @ 0x881f7b60    Exclusively owned
     Threads: 8802a790-01<*>

Resource @ 0x8824b418    Exclusively owned
    Contention Count = 34
     Threads: 8802a790-01<*>

Resource @ 0x8825e5a0    Exclusively owned
     Threads: 8802a790-01<*>

Resource @ 0x88172428    Exclusively owned
    Contention Count = 5
    NumberOfExclusiveWaiters = 1
     Threads: 8802a790-01<*>
     Threads Waiting On Exclusive Access:
              880f5020

 

계속 찾아보았을 때 스레드 8802a790이 스레드 880f5020이 리소스 89bd7bf0를 릴리즈 하기를 기다리고 있음을 알 수 있습니다:

Resource @ 0x89bd7bf0    Exclusively owned
    Contention Count = 1
    NumberOfExclusiveWaiters = 1
     Threads: 880f5020-01<*>
     Threads Waiting On Exclusive Access:
                   8802a790

 

자세히 살펴보면 우리는 위에서 이미 스레드 880f5020을 본 적이 있습니다. 그 부분을 다시 살펴봅시다(컬러로 표시):

Resource @ 0x88172428    Exclusively owned
    Contention Count = 5
    NumberOfExclusiveWaiters = 1
     Threads: 8802a790-01<*>
     Threads Waiting On Exclusive Access:
                  880f5020

우리는 스레드 880f5020이 스레드 8802a790을 기다리고 있고 스레드 8802a790은 스레드 880f5020을 기다리고 있는 것을 볼 수 있습니다.

이것으로 전통적인 형태의 데드락을 확인해보았습니다. 이제 우리가 해야 할 일은 이들 스레드들의 스택 트레이스를 통해서 어떤 컴포넌트들이 연관되어 있는지 알아내는 것입니다.

- Dmitry Vostokov @ DumpAnalysis.org -