Reject device control requests above PASSIVE_LEVEL
IOCTL_PIO_LOAD_BINARY runs the module signature check inline on the calling thread: vm_load_binary_internal calls check_signature, which calls calculate_sha256 and verify_sig, which call BCryptOpenAlgorithmProvider, BCryptCreateHash and BCryptImportKeyPair. Those require PASSIVE_LEVEL. IOCTL_PIO_EXECUTE_FN has the same requirement one step removed, since vm_execute_function takes the context's FAST_MUTEX. Nothing checked. A kernel-mode sender that built the request with IoBuildDeviceIoControlRequest and issued it at DISPATCH_LEVEL went straight into CNG at the wrong IRQL. Guard the whole IRP_MJ_DEVICE_CONTROL arm rather than the two ioctls, so a new ioctl cannot be added without the check. IRP_MJ_CREATE and IRP_MJ_CLOSE are deliberately left alone: CREATE only returns success, and failing CLOSE would leak the context and its pool. No user-mode caller is affected -- a device control IRP that originates from NtDeviceIoControlFile is always dispatched at PASSIVE_LEVEL. Confirmed by running the full test module against the installed build: ioctl_test_callback and ioctl_test both return success and PawnIOUtil exits 0. The rejection itself is not measured. Producing a DISPATCH_LEVEL sender means a second driver, and the obvious way to write one is a trap: an IRP built with a stack-local KEVENT and IO_STATUS_BLOCK is completed by a special kernel APC that runs after the frame is gone, which bugchecks 0x1E in nt!KiApcInterrupt with the caller's stack already destroyed. It is not worth another kernel probe to observe a four-line guard, so this rests on the code and on the regression run above.
N
namazso committed
9d2780667690ede585bbe86ac4f7ded6960d3b01
Parent: 46a58a4