Bugcheck 0xD6
드라이버 확인 프로그램(Driver Verifier)이 오류를 발견했을 때 발생시키는 버그체크 중 하나인 0xD6을 살펴보자.
책의 부록으로 첨부된 MEMORY.dmp 파일을 Windbg로 로드한다.
심볼과 소스코드 경로를 설정해준 뒤 !analyze -v 를 실행해보자.
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION (d6)
N bytes of memory was allocated and more than N bytes are being referenced.
This cannot be protected by try-except.
When possible, the guilty driver's name (Unicode string) is printed on
the bugcheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: 820cb000, memory referenced
Arg2: 00000001, value 0 = read operation, 1 = write operation
Arg3: f9d61706, if non-zero, the address which referenced memory.
Arg4: 00000000, (reserved)
Debugging Details:
------------------
KEY_VALUES_STRING: 1
PROCESSES_ANALYSIS: 1
SERVICE_ANALYSIS: 1
STACKHASH_ANALYSIS: 1
TIMELINE_ANALYSIS: 1
DUMP_CLASS: 1
DUMP_QUALIFIER: 401
BUILD_VERSION_STRING: 2600.xpsp_sp2_gdr.070227-2254
SYSTEM_MANUFACTURER: VMware, Inc.
VIRTUAL_MACHINE: VMware
SYSTEM_PRODUCT_NAME: VMware Virtual Platform
SYSTEM_VERSION: None
BIOS_VENDOR: Phoenix Technologies LTD
BIOS_VERSION: 6.00
BIOS_DATE: 08/15/2008
BASEBOARD_MANUFACTURER: Intel Corporation
BASEBOARD_PRODUCT: 440BX Desktop Reference Platform
BASEBOARD_VERSION: None
DUMP_TYPE: 1
BUGCHECK_P1: ffffffff820cb000
BUGCHECK_P2: 1
BUGCHECK_P3: fffffffff9d61706
BUGCHECK_P4: 0
WRITE_ADDRESS: Target machine operating system not supported
820cb000
FAULTING_IP:
MyDrv!BugCheckD6+16 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 346]
f9d61706 c6400800 mov byte ptr [eax+8],0
MM_INTERNAL_CODE: 0
IMAGE_NAME: MyDrv.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 4a253936
MODULE_NAME: MyDrv
FAULTING_MODULE: f9d61000 MyDrv
CPU_COUNT: 1
CPU_MHZ: 836
CPU_VENDOR: GenuineIntel
CPU_FAMILY: 6
CPU_MODEL: 17
CPU_STEPPING: 6
CPU_MICROCODE: 6,17,6,0 (F,M,S,R) SIG: 607'00000000 (cache) 607'00000000 (init)
DEFAULT_BUCKET_ID: DRIVER_FAULT
BUGCHECK_STR: 0xD6
PROCESS_NAME: MyApp.exe
ANALYSIS_SESSION_HOST: DESKTOP-E0TEGSR
ANALYSIS_SESSION_TIME: 02-20-2022 20:12:29.0947
ANALYSIS_VERSION: 10.0.18362.1 amd64fre
TRAP_FRAME: f786ec60 -- (.trap 0xfffffffff786ec60)
ErrCode = 00000002
eax=820caff8 ebx=827ecf00 ecx=00000000 edx=00000000 esi=81372040 edi=8136d9c0
eip=f9d61706 esp=f786ecd4 ebp=f786ecd8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
MyDrv!BugCheckD6+0x16:
f9d61706 c6400800 mov byte ptr [eax+8],0 ds:0023:820cb000=??
Resetting default scope
LAST_CONTROL_TRANSFER: from 80521656 to 804fbdeb
STACK_TEXT:
f786ebe0 80521656 00000050 820cb000 00000001 nt!KeBugCheckEx+0x1b
f786ec48 805457c8 00000001 820cb000 00000000 nt!MmAccessFault+0x9a8
f786ec48 f9d61706 00000001 820cb000 00000000 nt!KiTrap0E+0xd0
f786ecd8 f9d62008 0000005c e146c438 00000000 MyDrv!BugCheckD6+0x16 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 346]
f786fc1c 804f1095 8136d9c0 827ecf68 806e6428 MyDrv!MyDrvDeviceControl+0x3a8 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 561]
f786fc2c 80658128 811e5880 806e6410 827ecf68 nt!IopfCallDriver+0x31
f786fc50 8058070a 827ecfd8 814a3f90 827ecf68 nt!IovCallDriver+0xa0
f786fc64 8058156d 8136d9c0 827ecf68 814a3f90 nt!IopSynchronousServiceTail+0x60
f786fd00 8057a0c2 000000bc 00000000 00000000 nt!IopXxxControlFile+0x5c5
f786fd34 8054286c 000000bc 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f786fd34 7c93eb94 000000bc 00000000 00000000 nt!KiFastCallEntry+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f878 00000000 00000000 00000000 00000000 0x7c93eb94
THREAD_SHA1_HASH_MOD_FUNC: c8c0afdd9e4fd0d5d387fa9936efc7d8bafe6cc7
THREAD_SHA1_HASH_MOD_FUNC_OFFSET: 142df0d54e37b3e3dce85d7c59f811e72c8356ff
THREAD_SHA1_HASH_MOD: b9150089c125baa4b9b0cf9974281b5c03b45bc4
FOLLOWUP_IP:
MyDrv!BugCheckD6+16 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 346]
f9d61706 c6400800 mov byte ptr [eax+8],0
FAULT_INSTR_CODE: 840c6
FAULTING_SOURCE_LINE: e:\windbgcd\ch2\src\mydrv\mydrv.c
FAULTING_SOURCE_FILE: e:\windbgcd\ch2\src\mydrv\mydrv.c
FAULTING_SOURCE_LINE_NUMBER: 346
FAULTING_SOURCE_CODE:
342: void BugCheckD6(void)
343: {
344: PCHAR p = (PCHAR)ExAllocatePool( NonPagedPool, 8 );
345:
> 346: p[8] = 0;
347:
348: ExFreePool( p );
349: }
350:
351: void PrintBreakPoint(PCHAR pParamStr)
SYMBOL_STACK_INDEX: 3
SYMBOL_NAME: MyDrv!BugCheckD6+16
FOLLOWUP_NAME: MachineOwner
IMAGE_VERSION: 6.0.6000.16386
STACK_COMMAND: .thread ; .cxr ; kb
FAILURE_BUCKET_ID: 0xD6_VRF_MyDrv!BugCheckD6+16
BUCKET_ID: 0xD6_VRF_MyDrv!BugCheckD6+16
PRIMARY_PROBLEM_CLASS: 0xD6_VRF_MyDrv!BugCheckD6+16
TARGET_TIME: 2009-07-04T16:12:02.000Z
OSBUILD: 2600
OSSERVICEPACK: 2000
SERVICEPACK_NUMBER: 2
OS_REVISION: 0
SUITE_MASK: 272
PRODUCT_TYPE: 1
OSPLATFORM_TYPE: x86
OSNAME: Windows XP
OSEDITION: Windows XP WinNt (Service Pack 2) TerminalServer SingleUserTS
OS_LOCALE:
USER_LCID: 0
OSBUILD_TIMESTAMP: 2007-02-28 17:38:53
BUILDOSVER_STR: 5.1.2600.xpsp_sp2_gdr.070227-2254
ANALYSIS_SESSION_ELAPSED_TIME: 1990
ANALYSIS_SOURCE: KM
FAILURE_ID_HASH_STRING: km:0xd6_vrf_mydrv!bugcheckd6+16
FAILURE_ID_HASH: {823b7181-ec7e-7fe3-2f56-cef23ec0e1ec}
Followup: MachineOwner
---------
메시지 상 버그체크 제목이 DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION으로 나타난다.
그 아래 설명을 읽어보면, N 바이트 할당된 메모리인데, N 바이트보다 더 많이 읽으려고 시도했다는 내용이다.
드라이버 확인 프로그램(verifier)에서 특수 풀(Special Pool) 옵션을 켜면 이런 상황을 잡아준다.
드라이버 확인 프로그램이 켜져 있지 않았따면, 메모리가 이미 여기저기 깨지고 나서 한참 후에 시스템이 중지하므로 문제를 일으킨 순간을 알 수 없게 된다.
다행히 드라이버 확인 프로그램은 메모리를 깨는 순간 잡아주는 기능을 가지고 있으므로, 이 기능을 잘 활용하는 것이 좋다.
메시지 중간에 아래와 같이 trap 명령어가 있으므로, 실행해보자.
TRAP_FRAME: f786ec60 -- (.trap 0xfffffffff786ec60)
kd> .trap 0xfffffffff786ec60
ErrCode = 00000002
eax=820caff8 ebx=827ecf00 ecx=00000000 edx=00000000 esi=81372040 edi=8136d9c0
eip=f9d61706 esp=f786ecd4 ebp=f786ecd8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
MyDrv!BugCheckD6+0x16:
f9d61706 c6400800 mov byte ptr [eax+8],0 ds:0023:820cb000=??
kn 명령으로 콜 스택을 살펴보자.
kd> kn
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
00 f786ecd8 f9d62008 MyDrv!BugCheckD6+0x16 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 346]
01 f786fc1c 804f1095 MyDrv!MyDrvDeviceControl+0x3a8 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 561]
02 f786fc2c 80658128 nt!IopfCallDriver+0x31
03 f786fc50 8058070a nt!IovCallDriver+0xa0
04 f786fc64 8058156d nt!IopSynchronousServiceTail+0x60
05 f786fd00 8057a0c2 nt!IopXxxControlFile+0x5c5
06 f786fd34 8054286c nt!NtDeviceIoControlFile+0x2a
07 f786fd34 7c93eb94 nt!KiFastCallEntry+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
08 0012f878 00000000 0x7c93eb94
0번 프레임으로 이동해서 지역변수와 소스를 보자
kd> .frame 0n0;dv /t /v
00 f786ecd8 f9d62008 MyDrv!BugCheckD6+0x16 [e:\windbgcd\ch2\src\mydrv\mydrv.c @ 346]
f786ecd4 char * p = 0x820caff8 "%%%%%%%%"
0x820caff8 주소로부터 +8 주소(0x820cb008)에 0을 대입한 코드가 문제의 원인으로 보인다.
kd> db 820caff8
820caff8 25 25 25 25 25 25 25 25-?? ?? ?? ?? ?? ?? ?? ?? %%%%%%%%????????
820cb008 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb018 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb028 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb038 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb048 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb058 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
820cb068 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
메모리에서 ??로 보여지는 부분은 무엇일까?
BugCheck 0xD6은 드라이버 확인 프로그램에서 특수 풀(Special Memory Pool) 옵션을 켜야 발생한다.
특수 풀이란 메모리 할당, 해제, 사용에 대한 버그를 잡기 위해 특별히 사용되는 메모리 영역을 의미한다.
특수 풀은 메모리를 하나 할당할 때마다 최소 2페이지(8KB)를 사용한다.
첫 번째 페이지는 사용자가 사용하는 데이터 영역이고, 두 번째 페이지는 보호 페이지(Guard Page)다.
보호 페이지는 유효하지 않은 메모리 영역을 지정해 놓고 누군가 이 곳에 접근하면 페이지 폴트(Page Fault)가 발생하게 설정해 놓은 것이다.
이 것이 바로 ??로 나오는 부분의 의미다.
즉, 820CB000 부터가 보호 페이지라는 의미이다.
이번 예제는 할당된 메모리를 벗어나 보호 페이지 영역에 접근하면서 문제가 발생한 것이다.
특수 풀은 사용자가 1바이트만큼만 할당에도 실제로는 8KB의 메모리가 할당될 만큼 메모리 낭비가 심하므로 특수 풀로 예약된 영역을 모두 소진하면 특수 풀에서 할당하지 않고 일반 메모리에서 할당한다.
따라서 특수 풀이 항상 모든 문제를 잡아줄 수는 없다는 점도 참고해야 한다.