NULL Data Pointer Pattern: case study
Here is the promised case study for the previous post about data NULL pointers. The complete dump has this bugcheck:
0: kd> !analyze -v
[...]
KERNEL_MODE_EXCEPTION_NOT_HANDLED (8e)
This is a very common bugcheck. Usually the exception address pinpoints the driver/function that caused the problem. Always note this address as well as the link date of the driver/image that contains this address. Some common problems are exception code 0x80000003. This means a hard coded breakpoint or assertion was hit, but this system was booted /NODEBUG. This is not supposed to happen as developers should never have hardcoded breakpoints in retail code, but ... If this happens, make sure a debugger gets connected, and the system is booted /DEBUG. This will let us see why this breakpoint is happening.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: 8081c7c4, The address that the exception occurred at
Arg3: f1b5d730, Trap Frame
Arg4: 00000000
[...]
FAULTING_IP:
nt!IoIsOperationSynchronous+e
8081c7c4 f6412c02 test byte ptr [ecx+2Ch],2
TRAP_FRAME: f1b5d730 -- (.trap 0xfffffffff1b5d730)
[...]
0: kd> .trap 0xfffffffff1b5d730
ErrCode = 00000000
eax=8923b008 ebx=00000000 ecx=00000000 edx=8923b008 esi=891312d0 edi=89f0b300
eip=8081c7c4 esp=f1b5d7a4 ebp=f1b5d7a4 iopl=0 nv up ei ng nz ac pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296
nt!IoIsOperationSynchronous+0xe:
8081c7c4 f6412c02 test byte ptr [ecx+2Ch],2 ds:0023:0000002c=??
0: kd> kv 100
ChildEBP RetAddr Args to Child
f1b5d7a4 f42cdea9 8923b008 89f0b300 8923b008 nt!IoIsOperationSynchronous+0xe
f1b5d7bc 8081df85 89f0b300 8923b008 00000200 driveB!FsdDeviceIoControlFile+0×19
f1b5d7d0 808ed7a9 00000000 f1b5da84 f1b5db6c nt!IofCallDriver+0×45
f1b5da20 f3c3a521 89f0b300 f1b5da84 f1b5da84 nt!IoVolumeDeviceToDosName+0×89
WARNING: Stack unwind information not available. Following frames may be wrong.
f1b5da3c f3c3b58e 00000618 e4e00420 f1b5dad4 driverA+0×18531
[…]
f1b5dc3c 8081df85 89f48b48 87fa3008 89140d30 driverA+0×1df4
f1b5dc50 808f5437 87fa3078 89140d30 87fa3008 nt!IofCallDriver+0×45
f1b5dc64 808f61bf 89f48b48 87fa3008 89140d30 nt!IopSynchronousServiceTail+0×10b
f1b5dd00 808eed08 000000f0 00000000 00000000 nt!IopXxxControlFile+0×5e5
f1b5dd34 808897bc 000000f0 00000000 00000000 nt!NtDeviceIoControlFile+0×2a
f1b5dd34 7c8285ec 000000f0 00000000 00000000 nt!KiFastCallEntry+0xfc (TrapFrame @ f1b5dd64)
0856e154 7c826fcb 77e416f5 000000f0 00000000 ntdll!KiFastSystemCallRet
0856e158 77e416f5 000000f0 00000000 00000000 ntdll!NtDeviceIoControlFile+0xc
0856e1bc 6f050c6c 000000f0 5665824c 0856e234 kernel32!DeviceIoControl+0×137
[…]
From WDK help we know that the first parameter to IoIsOperationSynchronous is a pointer to an IRP structure:
0: kd> !irp 8923b008
Irp is active with 3 stacks 3 is current (= 0x8923b0c0)
No Mdl: System buffer=878b7288: Thread 8758a020: Irp stack trace.
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000
Args: 00000000 00000000 00000000 00000000
>[ e, 0] 0 0 89f0b300 00000000 00000000-00000000
\FileSystem\DriverB
Args: 00000200 00000000 004d0008 00000000
Disassembling the function shows some pointer dereferencing and we can reconstruct it starting from EBP+8, a pointer to an IRP.
0: kd> .asm no_code_bytes
Assembly options: no_code_bytes
0: kd> u nt!IoIsOperationSynchronous nt!IoIsOperationSynchronous+0xe
nt!IoIsOperationSynchronous:
8081c7b6 mov edi,edi
8081c7b8 push ebp
8081c7b9 mov ebp,esp
8081c7bb mov eax,dword ptr [ebp+8]
8081c7be mov ecx,dword ptr [eax+60h]
8081c7c1 mov ecx,dword ptr [ecx+18h]
EAX+60 seems to be a current stack location member of IRP and it is a pointer itself to _IO_STACK_LOCATION structure:
0: kd> dt -r _IRP 8923b008
ntdll!_IRP
+0x000 Type : 6
+0x002 Size : 0x268
+0x004 MdlAddress : (null)
+0x008 Flags : 0x70
[...]
+0x038 CancelRoutine : (null)
+0x03c UserBuffer : 0xf1b5d814
+0×040 Tail : __unnamed
+0×000 Overlay : __unnamed
+0×000 DeviceQueueEntry : _KDEVICE_QUEUE_ENTRY
+0×000 DriverContext : [4] (null)
+0×010 Thread : 0×8758a020 _ETHREAD
+0×014 AuxiliaryBuffer : (null)
+0×018 ListEntry : _LIST_ENTRY [ 0×0 - 0×0 ]
+0×020 CurrentStackLocation : 0×8923b0c0 _IO_STACK_LOCATION
[…]
ECX+18 is a pointer to a file object in _IO_STACK_LOCATION structure:
0: kd> dt _IO_STACK_LOCATION 8923b008+60
ntdll!_IO_STACK_LOCATION
+0x000 MajorFunction : 0xc0 ''
+0x001 MinorFunction : 0xb0 ''
+0x002 Flags : 0x23 '#'
+0x003 Control : 0x89 ''
+0x004 Parameters : __unnamed
+0x014 DeviceObject : (null)
+0×018 FileObject : (null)
+0×01c CompletionRoutine : (null)
+0×020 Context : (null)
2C offset at the crash point test byte ptr [ecx+2Ch],2 is _FILE_OBJECT Flags member:
0: kd> dt _FILE_OBJECT
ntdll!_FILE_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Vpb : Ptr32 _VPB
+0x00c FsContext : Ptr32 Void
+0x010 FsContext2 : Ptr32 Void
+0x014 SectionObjectPointer : Ptr32 _SECTION_OBJECT_POINTERS
+0x018 PrivateCacheMap : Ptr32 Void
+0x01c FinalStatus : Int4B
+0x020 RelatedFileObject : Ptr32 _FILE_OBJECT
+0x024 LockOperation : UChar
+0x025 DeletePending : UChar
+0x026 ReadAccess : UChar
+0x027 WriteAccess : UChar
+0x028 DeleteAccess : UChar
+0x029 SharedRead : UChar
+0x02a SharedWrite : UChar
+0x02b SharedDelete : UChar
+0×02c Flags : Uint4B
+0×030 FileName : _UNICODE_STRING
+0×038 CurrentByteOffset : _LARGE_INTEGER
+0×040 Waiters : Uint4B
+0×044 Busy : Uint4B
+0×048 LastLock : Ptr32 Void
+0×04c Lock : _KEVENT
+0×05c Event : _KEVENT
+0×06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT
So it looks like driverA passed an IRP with NULL File object address to driverB and this is also shown in the output of !irp command above.
- Dmitry Vostokov @ DumpAnalysis.org -