Understand driver verifier bug check - Kernel Zw API called with user-mode address..

Hi,

I have in my filesystem filter driver a bugcheck:
DRIVER_VERIFIER_DETECTED_VIOLATION (c4) -
Arg1: 00000000000000e3, Kernel Zw API called with user-mode address as parameter.
Arg2: fffff8002afba2e0, Address inside the driver making the incorrect API call.
Arg3: 00000000005c82a8, User-mode address used as API parameter.

Arg3 mentioned here is the inputBuffer (from user mode) that represents the device to open passed to ZwCreateFile:

STACK_TEXT:
nt!DbgBreakPointWithStatus
nt!KiBugCheckDebugBreak+0x12
nt!KeBugCheck2+0x8ab
nt!KeBugCheckEx+0x104
nt!VerifierBugCheckIfAppropriate+0x3c
nt!ViZwCheckAddress+0x35
nt!ViZwCheckUnicodeString+0x2e
nt!ViZwCheckObjectAttributes+0x26
nt!VfZwCreateFile+0x5d
MYDRV!GetPdoDeviceFromFdo
MYDRV!DrvFastIoDeviceControl
nt!IopXxxControlFile+0x7c2
nt!NtDeviceIoControlFile+0x56
nt!KiSystemServiceCopyEnd+0x13
wow64cpu!CpupSyscallStub+0x2
wow64cpu!DeviceIoctlFileFault+0x31
wow64!RunCpuSimulation+0xa
wow64!Wow64LdrpInitialize+0x172
ntdll!_LdrpInitialize+0xcb
ntdll!LdrInitializeThunk+0xe

The call from the stack that triggers bugcheck is (from function GetPdoDeviceFromFdo):

RtlInitUnicodeString(&devicePath, (LPWSTR)inputBuffer);
InitializeObjectAttributes(&objectAttributes, &devicePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ntStatus = ZwCreateFile(&fileHandle, SYNCHRONIZE | FILE_ANY_ACCESS, &objectAttributes, &status, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

0: kd> dv
inputBuffer = 0x00000000005c82a8 -- this is the address verifier "seems" to compalin !! outputBuffer = 0x000000000389f79c
outputBufferLength = 0x411
devicePath = “??\Volume{b90196ba-3f1c-11e6-9bfa-08002781e119}”

Device Path is created based on inputBuffer and passed as objectAttributes to ZwCreateFile. What could go wrong about this ?

Debugging this it seems to happen while system is starting, I saw times where this call was successful using same devicePath (verifier was not enabled). Or maybe other parameters are wrong, not what is hinted by verifier ?

This call happens as part of a user mode IO control defined for our driver communication device. One unusual thing is that this is using fast io (fastIoDispatch->FastIoDeviceControl = DrvFastIoDeviceControl) The control code was defined like this:

#define MyDrv_getpdodev (ULONG) CTL_CODE(IOCTL_UNKNOWN_BASE, 0x815, METHOD_BUFFERED, FILE_ANY_ACCESS)

What I find strange is that the inputBuffer is a valid user mode address (but ioctl uses method buffered). The callstack is running at irql PASSIVE so that should not be an issue.

Any hints to check will be welcomed !
Regards
-Ghita

When you call a Zw API, the previous/requestor mode is set to Kernel. This
means that all buffer validation is turned off for the call (amongst other
things). Therefore, it is a Really Bad Idea to pass a user mode buffer to a
Zw call and Verifier has added a check for it.

Fast I/O is always effectively METHOD_NEITHER.

-scott
OSR
@OSRDrivers

wrote in message news:xxxxx@ntdev…

Hi,

I have in my filesystem filter driver a bugcheck:
DRIVER_VERIFIER_DETECTED_VIOLATION (c4) -
Arg1: 00000000000000e3, Kernel Zw API called with user-mode address as
parameter.
Arg2: fffff8002afba2e0, Address inside the driver making the incorrect API
call.
Arg3: 00000000005c82a8, User-mode address used as API parameter.

Arg3 mentioned here is the inputBuffer (from user mode) that represents the
device to open passed to ZwCreateFile:

STACK_TEXT:
nt!DbgBreakPointWithStatus
nt!KiBugCheckDebugBreak+0x12
nt!KeBugCheck2+0x8ab
nt!KeBugCheckEx+0x104
nt!VerifierBugCheckIfAppropriate+0x3c
nt!ViZwCheckAddress+0x35
nt!ViZwCheckUnicodeString+0x2e
nt!ViZwCheckObjectAttributes+0x26
nt!VfZwCreateFile+0x5d
MYDRV!GetPdoDeviceFromFdo
MYDRV!DrvFastIoDeviceControl
nt!IopXxxControlFile+0x7c2
nt!NtDeviceIoControlFile+0x56
nt!KiSystemServiceCopyEnd+0x13
wow64cpu!CpupSyscallStub+0x2
wow64cpu!DeviceIoctlFileFault+0x31
wow64!RunCpuSimulation+0xa
wow64!Wow64LdrpInitialize+0x172
ntdll!_LdrpInitialize+0xcb
ntdll!LdrInitializeThunk+0xe

The call from the stack that triggers bugcheck is (from function
GetPdoDeviceFromFdo):

RtlInitUnicodeString(&devicePath, (LPWSTR)inputBuffer);
InitializeObjectAttributes(&objectAttributes, &devicePath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ntStatus = ZwCreateFile(&fileHandle, SYNCHRONIZE | FILE_ANY_ACCESS,
&objectAttributes, &status, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

0: kd> dv
inputBuffer = 0x00000000005c82a8 -- this is the address verifier "seems" to compalin !! outputBuffer = 0x000000000389f79c
outputBufferLength = 0x411
devicePath = “??\Volume{b90196ba-3f1c-11e6-9bfa-08002781e119}”

Device Path is created based on inputBuffer and passed as objectAttributes
to ZwCreateFile. What could go wrong about this ?

Debugging this it seems to happen while system is starting, I saw times
where this call was successful using same devicePath (verifier was not
enabled). Or maybe other parameters are wrong, not what is hinted by
verifier ?

This call happens as part of a user mode IO control defined for our driver
communication device. One unusual thing is that this is using fast io
(fastIoDispatch->FastIoDeviceControl = DrvFastIoDeviceControl) The control
code was defined like this:

#define MyDrv_getpdodev (ULONG) CTL_CODE(IOCTL_UNKNOWN_BASE, 0x815,
METHOD_BUFFERED, FILE_ANY_ACCESS)

What I find strange is that the inputBuffer is a valid user mode address
(but ioctl uses method buffered). The callstack is running at irql PASSIVE
so that should not be an issue.

Any hints to check will be welcomed !
Regards
-Ghita

Yeah, after posting the question i saw that this check was introduced after win8 if I’m not wrong. I think with Zw functions there is also the potential that giving them a user mode buffer if the operation gets pended in the kernel than the initial user mode context will be lost.

at first I was wandering if verifier can detect that the driver does buffer validation with ProbeMemory, in which case the reason for “security” bugcheck will not hold any more…

From a security perspective the ProbeForXxx alone is not sufficient when
dealing with user mode buffers, you must also:

  1. Wrap all accesses in a __try/__except block (just because the address was
    valid at Probe time doesn’t mean it will stay valid)

  2. Protect against time of check to time of use bugs (the user still has a
    “live” pointer to the data)

-scott
OSR
@OSRDrivers

wrote in message news:xxxxx@ntdev…

Yeah, after posting the question i saw that this check was introduced after
win8 if I’m not wrong. I think with Zw functions there is also the potential
that giving them a user mode buffer if the operation gets pended in the
kernel than the initial user mode context will be lost.

at first I was wandering if verifier can detect that the driver does buffer
validation with ProbeMemory, in which case the reason for “security”
bugcheck will not hold any more…

>at first I was wandering if verifier can detect that the driver does buffer validation with ProbeMemory, in which case the reason for “security” bugcheck will not hold any more…

Clearly, a bugcheck means that the verifier cannot detect whether the buffer has been validated by the driver or not.

Now if you allocate a paged pool (or nonpaged pool) buffer (call it SystemBuffer) that has the length of you InputBuffer, and then copy the content of InputBuffer into SystemBuffer, you know that you have to perform this copy within __try/__except blocks because you should never trust a user-mode buffer. So validation is performed and a system-space buffer is passed to the kernel. The verifier should not complain.

> When you call a Zw API, the previous/requestor mode is set to Kernel. This

means that all buffer validation is turned off for the call (amongst other
things). Therefore, it is a Really Bad Idea to pass a user mode buffer to a
Zw call and Verifier has added a check for it.

More so: for user handles, this Verifier check is here from Win7 up.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

I must add to my previous statement: ProbeForRead/ProbeForWrite is mandatory there. A copy is not a validation of a buffer although it seems that parameters are validated earlier in the stack.