Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

gflags와 _CrtIsMemoryBlock을 이용한 Heap 메모리 손상 여부 체크

지난 글에서,

windbg 분석 사례 - ODP.NET 사용 시 Finalizer에서 System.AccessViolationException 예외 발생으로 인한 비정상 종료
; https://www.sysnet.pe.kr/2/0/11785

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

CoTaskMemFree에서 발생한 예외의 원인이 Heap이 깨진 것이라고 설명했습니다.

ntdll!RtlReportCriticalFailure+97
ntdll!RtlpHeapHandleError+12
ntdll!RtlpLogHeapFailure+96
ntdll!RtlFreeHeap+823
OraOps19!ssmem_free+f
OraOps19!OpsMetFreeValCtx+89
DomainNeutralILStubClass.IL_STUB_PInvoke(Oracle.DataAccess.Client.OpoMetValCtx*)+6e
Oracle.DataAccess.Client.MetaData.Finalize()+25

결국 해결을 하긴 했지만,

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

그래도 혹시나 이 문제가 제가 만든 응용 프로그램에서 발생했다고 가정하고 문제를 빠르게 찾을 수 있는 방법이 없을까 고민을 하게 되었는데요. 가령 지난 글에서 사용한 예제 코드의 경우,

AcLayers의 API 후킹과 FaultTolerantHeap
; https://www.sysnet.pe.kr/2/0/12050

char *cp = new char[10];
(*(cp - 5))++;

CRT Heap의 헤더를 깨먹은 것이기 때문에 "delete cp;"를 수행 시 비정상 종료할 수 있습니다. C++의 경우에는 대개 할당을 한 함수 내에서 해제도 함께 하는 경우가 많기 때문에 문제를 쉽게 발견할 수 있는데, 반면 C#처럼 Finalizer로 인해 해제되는 경우에는 원인 분석이 쉽지 않습니다.




이런 유의 원인 분석을 위해 도움이 되는 것이 있다면 gflags의 heap 관련 옵션을 켜는 것입니다.

Detecting Heap Corruption Using GFlags and Dumps
; https://blogs.msdn.microsoft.com/webdav_101/2010/06/22/detecting-heap-corruption-using-gflags-and-dumps/

Debugging Heap corruption with Application Verifier and Debugdiag
; https://blogs.msdn.microsoft.com/lagdas/2008/06/24/debugging-heap-corruption-with-application-verifier-and-debugdiag/

gflags.exe -p /enable w3wp.exe /full
gflags.exe -p /enable ConsoleApplication1.exe /full

그러고 보니 예전에 ^^ 0xBAADF00D 쿠키를 확인하기 위해 사용해 본 적이 있었는데요.

Win32 Debug CRT Heap Internals의 0xBAADF00D 표시 재현
; https://www.sysnet.pe.kr/2/0/11255

옵션에 따라 할당 시점의 call stack을 가지고 있기 때문에 free/delete를 수행한 시점에 예외가 발생해도 어떤 메모리를 위해 할당한 것인지 구분할 수 있어 그와 연관된 코드를 좀 더 세심하게 찾아보는데 도움은 될 수 있습니다.

하지만, 저런 식으로 하면 Heap 관련 부가 정보가 더 점유하기 때문에 이전에는 CRT Header를 깨먹은 것도 gflags 이후에는 header가 아닐 수 있어 오히려 문제가 감춰지는 경우도 있습니다. (실제로 위의 예제 코드가 그런 경우입니다.) 이런저런 이유로 인해 이런 유의 문제를 해결하는 정답은 없습니다. 운이 좋다면, 깨진 header에 저장된 데이터를 보고... 만약 그것이 "파일 경로"인 듯한 데이터가 들어 있다면 프로그램에서 관련 데이터를 썼을 것 같은 코드를 범인으로 유추해 나가는 정도가 됩니다.

부가적으로 (Windows Heap이 아닌) CRT heap의 경우 _CrtIsMemoryBlock이라는 디버그 빌드에서만 사용 가능한 함수가 있습니다.

_CrtIsMemoryBlock
; https://learn.microsoft.com/ko-kr/cpp/c-runtime-library/reference/crtismemoryblock?view=vs-2019

그래도 이게 그나마 쓸만한데요, 다음과 같이 사용하면,

long requestNumber;
char* filename[MAX_PATH];
int linenumber;

printf("_CrtIsMemoryBlock == %d\n", _CrtIsMemoryBlock(cp, sizeof(char) * 10, &requestNumber,
    filename, &linenumber));

"(*(cp - 5))++;"로 깨진 바로 저 포인터를 넘겨서 해당 메모리가 유효한 CRT Heap의 구조를 가지고 있는지 체크해 줍니다. 물론, 그렇다고 해서 Header를 깬 위치를 알 수 있는 것은 아니고 의심이 가는 부분에 디버깅 용으로 저 코드를 심어놓고 assert 등의 처리를 해야 합니다.




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







[최초 등록일: ]
[최종 수정일: 3/9/2024]

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

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  [70]  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
11892정성태5/10/201912521웹: 38. HTTP Cookie의 expires 시간 형식(RFC7231)
11891정성태5/9/201915007.NET Framework: 831. (번역글) .NET Internals Cookbook Part 12 - Memory structure, attributes, handles
11890정성태5/8/201910765개발 환경 구성: 439. "Visual Studio Enterprise is required to execute the test." 메시지와 관련된 코드 기록
11889정성태5/8/201911614개발 환경 구성: 438. mstest, QTAgent의 로그 파일 설정 방법
11888정성태5/8/201926791.NET Framework: 830. C# - 비동기 호출을 취소하는 CancellationToken의 간단한 예제 코드 [1]파일 다운로드1
11887정성태5/8/201913316.NET Framework: 829. C# - yield 문을 사용할 수 있는 메서드의 조건
11886정성태5/7/201912390오류 유형: 534. mstest.exe 실행 시 "Visual Studio Enterprise is required to execute the test." 오류 [2]
11885정성태5/7/20199351오류 유형: 533. mstest.exe 실행 시 "File extension specified '.loadtest' is not a valid test extension." 오류 발생
11884정성태5/5/201913045.NET Framework: 828. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 두 번째 이야기
11883정성태5/3/201917344.NET Framework: 827. C# - 인터넷 시간 서버로부터 받은 시간을 윈도우에 적용하는 방법파일 다운로드1
11882정성태5/2/201914271.NET Framework: 826. (번역글) .NET Internals Cookbook Part 11 - Various C# riddles파일 다운로드1
11881정성태4/28/201914516오류 유형: 532. .NET Core 프로젝트로 마이그레이션 시 "CS0579 Duplicate 'System.Reflection.AssemblyCompanyAttribute' attribute" 오류 발생
11880정성태4/25/201911217오류 유형: 531. 이벤트 로그 오류 - Task Scheduling Error: m->NextScheduledSPRetry 1547, m->NextScheduledEvent 1547
11879정성태4/24/201916452.NET Framework: 825. (번역글) .NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others파일 다운로드2
11878정성태4/22/201914458.NET Framework: 824. (번역글) .NET Internals Cookbook Part 9 - Finalizers, queues, card tables and other GC stuff파일 다운로드1
11877정성태4/22/201914411.NET Framework: 823. (번역글) .NET Internals Cookbook Part 8 - C# gotchas파일 다운로드1
11876정성태4/21/201914229.NET Framework: 822. (번역글) .NET Internals Cookbook Part 7 - Word tearing, locking and others파일 다운로드1
11875정성태4/21/201914603오류 유형: 530. Visual Studo에서 .NET Core 프로젝트를 열 때 "One or more errors occurred." 오류 발생
11874정성태4/20/201914623.NET Framework: 821. (번역글) .NET Internals Cookbook Part 6 - Object internals파일 다운로드1
11873정성태4/19/201913582.NET Framework: 820. (번역글) .NET Internals Cookbook Part 5 - Methods, parameters, modifiers파일 다운로드1
11872정성태4/17/201914045.NET Framework: 819. (번역글) .NET Internals Cookbook Part 4 - Type members파일 다운로드1
11871정성태4/16/201913888.NET Framework: 818. (번역글) .NET Internals Cookbook Part 3 - Initialization tricks [3]파일 다운로드1
11870정성태4/16/201911564.NET Framework: 817. Process.Start로 실행한 콘솔 프로그램의 출력 결과를 얻는 방법파일 다운로드1
11869정성태4/15/201915418.NET Framework: 816. (번역글) .NET Internals Cookbook Part 2 - GC-related things [2]파일 다운로드2
11868정성태4/15/201913135.NET Framework: 815. CER(Constrained Execution Region)이란?파일 다운로드1
11867정성태4/15/201912156.NET Framework: 814. Critical Finalizer와 SafeHandle의 사용 의미파일 다운로드1
... 61  62  63  64  65  66  67  68  69  [70]  71  72  73  74  75  ...