Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 6개 있습니다.)
디버깅 기술: 130. windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12058

디버깅 기술: 131. windbg/Visual Studio - HeapFree x86의 동작 분석
; https://www.sysnet.pe.kr/2/0/12059

디버깅 기술: 132. windbg/Visual Studio - HeapFree x64의 동작 분석
; https://www.sysnet.pe.kr/2/0/12060

디버깅 기술: 133. windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/12062

디버깅 기술: 138. windbg와 Win32 API로 알아보는 Windows Heap 정보 분석
; https://www.sysnet.pe.kr/2/0/12068

디버깅 기술: 144.  windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12086




windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례 - 두 번째 이야기

지난번 덤프 분석을,

windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12058

사전 준비도 되었으니,

windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; http://sysnet.pe.kr/2/0/12058

windbg/Visual Studio - HeapFree x86의 동작 분석
; http://sysnet.pe.kr/2/0/12059

windbg/Visual Studio - HeapFree x64의 동작 분석
; http://sysnet.pe.kr/2/0/12060

다시 한번 가보겠습니다. ^^ 우선, callstack의 Finalizer에서 예외가 발생했던(OraOps19.dll!00007ffcf0628a29로 시작했던) 덤프를 먼저 분석할 텐데 Visual Studio로 해당 덤프를 열어 "Debug with Mixed"로,

debug_mixed_in_vs_1.png

시작하면 곧바로 다음과 같이 crash 당시에 발생한 예외를 인식해 창을 띄워줍니다.

debug_mixed_in_vs_2.png

Unhandled exception at 0x00007FFD05D882D3 (ntdll.dll) in dump4580.dmp: 0xC0000374: A heap has been corrupted (parameters: 0x00007FFD05DDF6B0).


오호~~~ "windbg/Visual Studio - HeapFree x64의 동작 분석" 글에서 설명했던 바로 그 "parameters" 정보입니다. 따라서 해당 주소를 메모리 창(Ctrl + Alt + M, 1)에서 보면,

vs_heap_free_3.png

0x00007FFD05DDF6B0  000006e000000002 
0x00007FFD05DDF6B8  0000000000000004 
0x00007FFD05DDF6C0  0000014e8cba0000 // hHandle
0x00007FFD05DDF6C8  00000152e6608b40 // (pVoid - 0x10) 위치
0x00007FFD05DDF6D0  0000000000000000 
0x00007FFD05DDF6D8  0000000000000000 
0x00007FFD05DDF6E0  0000000000000000 
0x00007FFD05DDF6E8  00000152e66045e0 
0x00007FFD05DDF6F0  00000152e660c5f0 
0x00007FFD05DDF6F8  0000000000000000 
0x00007FFD05DDF700  0000000000000000 

감각적으로 ^^ HeapFree로 해제하려는 대상 Heap Handle과 메모리 위치를 알 수 있습니다. 사실 지난 글에서는 9번째 슬롯에 있는 값이 "pVoid - 0x10" 위치였는데 이번에는 4번째로 바뀌었습니다. (이런 건 OS 버전 또는 Patch의 차이일 수 있으니 센스 있게 낚으시면 됩니다. ^^)

참고로, (HeapCreate로 생성된) hHandle의 경우 운영체제에서 메모리 할당 시 Page 크기에 정렬되므로 마지막 4바이트가 "0000"으로 끝나는 것을 보고 그나마 쉽게 알아볼 수 있습니다. 또는, 더 확실하게 hHandle을 확정하고 싶다면 역시 지난 글에서 보여준 코드에 따라,

00007FFDF6EFFBCF 49 8B F8             mov         rdi,r8    // rdi == r8 == pVoid1
00007FFDF6EFFBD2 8B F2                mov         esi,edx   // esi == edx == 0
00007FFDF6EFFBD4 48 8B D9             mov         rbx,rcx   // rbx == rcx == hHandle
00007FFDF6EFFBD7 4D 85 C0             test        r8,r8  
00007FFDF6EFFBDA 74 52                je          RtlFreeHeap+6Eh (07FFDF6EFFC2Eh)  
00007FFDF6EFFBDC 48 85 C9             test        rcx,rcx  
00007FFDF6EFFBDF 0F 84 29 50 07 00    je          memset+11C4Eh (07FFDF6F74C0Eh)  
00007FFDF6EFFBE5 81 7B 10 EE DD EE DD cmp         dword ptr [rbx+10h],0DDEEDDEEh // dword([rbx + 10h]) == 0xffeeffee

[hHandle + 0x10] 위치의 (SegmentSignature라고 불리는) dword 값이 ffeeffee 또는 ddeeddee인 것을 보고 확인하면 됩니다.

0x0000014E8CBA0000  0000000000000000 
0x0000014E8CBA0008  0100eba53f7de08a 
0x0000014E8CBA0010  00000002ffeeffee // hHandle + 0x10 위치
0x0000014E8CBA0018  0000014e8d650018 
0x0000014E8CBA0020  0000014e8cba0120

또한, (pVoid - 0x10) 위치 값을 더 신뢰하고 싶다면 역시 지난 글에서 call stack의 프레임 문맥 중 레지스터에 저장된 정보가 있었으므로,

[ntdll.dll!RtlpLogHeapFailure()]
rbx == 0000014E8CBA0000 == hHandle
rsi == rdi == 00000152E6608B40 == (pVoid - 0x10)

[ntdll.dll!RtlFreeHeap()]
r14 == 0000014E8CBA0000 == hHandle
rsi == 00000152E6608B40 == (pVoid - 0x10)
rdi == 00000152E6608B50 == pVoid

확률을 높이는데 참고하시면 됩니다. 끝입니다, 이제 다음과 같이 해당 주소로 메모리 창을 통해 살펴보면 헤더가 "8b00000000000000"으로 깨져 있는 것이 보이고, 이후 공백 문자로 시작하는 SQL 쿼리가 보입니다. 이와 함께 문자열을 좀 더 쉽게 식별하기 위해 "Watch" 창에 "(char *)...address..."를 입력하면 "Text Visualizer"를 통해 완전한 문자열을 구할 수 있습니다. (보는 바와 같이 SQL 쿼리가 저장되어 있습니다.)

debug_mixed_in_vs_3.png

분석은 여기까지입니다. 도대체 누가 어디서 8b00000000000000 값을 CoTaskMemAlloc으로 할당받은 메모리의 헤더에 덮어썼는지는 (snapshot에 불과한 메모리 덤프로는) 알 수 없습니다.




그다음 Marshal.FreeCoTaskMem callstack을 가진 두 번째 덤프도 한 번 볼까요? ^^ 역시 Visual Studio로 열면 다음과 같은 예외 메시지의 창이 뜹니다.

Unhandled exception at 0x00007FFD05D882D3 (ntdll.dll) in dump496.dmp: 0xC0000374: A heap has been corrupted (parameters: 0x00007FFD05DDF6B0). occurred


이젠 뭐 익숙해졌으니 ^^ 0x00007FFD05DDF6B0 값을 보면,

0x00007FFD05DDF6B0  000006e000000002 
0x00007FFD05DDF6B8  0000000000000004 
0x00007FFD05DDF6C0  00000286f28f0000 // hHandle
0x00007FFD05DDF6C8  0000028b412d3be0 // pVoid - 0x10
0x00007FFD05DDF6D0  0000000000000000 
0x00007FFD05DDF6D8  0000000000000000 
0x00007FFD05DDF6E0  0000000000000000 
0x00007FFD05DDF6E8  0000028b412cc9d0 
0x00007FFD05DDF6F0  0000028b412d49e0 

0000028b412d3be0 주솟값을 구했고 이 위치의 메모리 내용을 살펴 보니,

0000028b412d3be0 0000000000000000
0000028b412d3be8 8b00000000000000 == header 깨짐
0000028b412d3bf0 202020202020200a == pVoid 위치
0000028b412d3bf8 6c65732020202020
0000028b412d3c00 44492e2020202020

오호... 놀랍군요. ^^ 헤더를 깨뜨린 바이트 패턴(8b00000000000000)도 동일하고, 심지어 해당 메모리에 담겼던 SQL 쿼리 문자열도 동일합니다.




이제 문제를 좀 더 특정하기 위해, Oracle.DataAccess.Client의 ExecuteReader 메서드를 보겠습니다. 다행히, FreeCoTaskMem 메서드가 호출되는 곳은 하나뿐이어서 쉽게 문제의 코드를 찾아낼 수 있습니다.

try
{
    bool flag3 = ptr != null && ptr->pNewCommandText != IntPtr.Zero;
    num4 = OpsSql.Prepare2(this.m_opsConCtx, ref this.m_opsErrCtx, ref this.m_opsSqlCtx, 
        ref this.m_opsDacCtx, ref this.m_pOpoSqlValCtx, flag3 ? null : this.m_pooledCmdText, ref zero2, ref ptr, num5);
}
catch (Exception ex)
{
        // ...[생략]...
}
finally
{
    this.m_executeScalar = false;
    if (zero2 != IntPtr.Zero)
    {
        try
        {
            Marshal.FreeCoTaskMem(zero2);
        }
        // ...[생략]...
    }
    // ...[생략]...
}

그러니까, 위의 코드를 정리해 보면 string 타입인 this.m_pooledCmdText 필드에 있는 SQL 쿼리 텍스트가 (Native 모듈인 OraOps11w.dll에서 export한 OpsSqlPrepare2 함수를 호출하는) OpsSql.Prepare2 내에서 CoTaskMemAlloc을 할당받고 네이티브 heap에 쿼리 문자열이 복사된 다음 그 메모리 위치를 zero2 변수에 반환해 주는 구조입니다.

일단, 여기까지의 내용을 보면... 분명히 Oracle 쪽의 ODAC에 문제가 있는 듯합니다. 왜냐하면, 할당받은 메모리가 저 사이에 동일한 패턴으로 다른 스레드에 의해 침범을 당한다거나... 하는 확률은 극히 낮다고 볼 수 있기 때문입니다.




해당 고객사도 Oracle 쪽에 이슈를 제기한 상황이라고 하니, ^^ 혹시나 후기가 들려오면 업데이트하겠습니다.

(2019-12-20 업데이트: 이 문제의 원인은 windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례 글에서 설명합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/7/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 151  152  153  154  155  156  157  158  159  160  161  [162]  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
999정성태2/28/201146586개발 환경 구성: 108. RemoteFX - Windows 7 가상 머신에서 DirectX 9c 환경을 제공 [5]
998정성태2/27/201120286Team Foundation Server: 42. TFS Application-Tier만 재설치
996정성태2/12/201137737디버깅 기술: 35. windbg - 분석 예: 시작하자마자 비정상 종료하는 프로세스 - NullReferenceException
995정성태2/11/201156258.NET Framework: 205. 코드(C#)를 통한 풀 덤프 만드는 방법 [4]
994정성태2/10/201136213디버깅 기술: 34. Windbg - ERROR: Unable to load DLL mscordacwks_x86_x86_2.0.50727.4200.dll, Win32 error 0n2 [1]
993정성태2/10/201128526개발 환경 구성: 107. 하나의 WPF 프로젝트를 WinExe / Library로 빌드하는 방법
992정성태10/15/201129379개발 환경 구성: 106. VSS(Volume Shadow Service)를 이용한 Hyper-V VM 백업/복원 [2]
991정성태2/6/201148857개발 환경 구성: 105. 풀 덤프 파일을 남기는 방법 [4]
990정성태2/2/201134003개발 환경 구성: 104. Visual C++ Custom Build Tool 사용예 [1]파일 다운로드1
989정성태2/1/201130515개발 환경 구성: 103. DOS batch - 동기 방식으로 원격 서비스 제어
988정성태1/30/201126705개발 환경 구성: 102. MSBuild - DefineConstants에 다중 전처리 값 설정
987정성태1/29/201139861디버깅 기술: 33. PDB Symbol 로드 오류 - Cannot find or open the PDB file. [2]
986정성태1/26/201131311.NET Framework: 204. 분리된 ThreadPool 사용 - Smart Thread Pool
985정성태1/25/201127947디버깅 기술: 32. 인증서로 서명된 닷넷 어셈블리의 실행 지연 현상
984정성태1/25/201122659개발 환경 구성: 101. SharePoint 2010 - Form Design
983정성태1/23/201127720제니퍼 .NET: 15. 눈으로 확인하는 maxWorkerThreads, minFreeThreads 설정값 [1]
982정성태1/22/201125012개발 환경 구성: 100. SharePoint 2010 - iPad 친화적인 게시판 만들기 (사용자 지정 목록) [1]
981정성태1/19/201120889개발 환경 구성: 99. SharePoint 2010 - 웹 애플리케이션 생성 시 고려해야 할 점. [1]
980정성태1/19/201132383개발 환경 구성: 98. SharePoint 2010 - Office Web Apps 설치
979정성태1/18/201125006개발 환경 구성: 97. SharePoint 2010 팀 사이트 구성
978정성태1/16/201131972.NET Framework: 203. VPN 자동 연결 및 Router 설정 추가
977정성태1/12/201131307개발 환경 구성: 96. SharePoint 2010 설치 [5]
976정성태1/11/201153951오류 유형: 111. IIS - 500.19 오류 (0x8007000d)
975정성태1/10/201128210.NET Framework: 202. CLR JIT 컴파일러가 생성한 기계어 코드 확인하는 방법 [3]파일 다운로드1
974정성태1/8/201126981.NET Framework: 201. 윈폼 TreeView - Bold 폰트 설정 후 텍스트가 잘리는 문제 [1]파일 다운로드1
973정성태1/7/201126316.NET Framework: 200. IIS Metabase와 ServerManager 개체 활용파일 다운로드1
... 151  152  153  154  155  156  157  158  159  160  161  [162]  163  164  165  ...