I am making a lower filter driver for a custom mouse device. I was able to forward the EvtIoRead requests I got from mouhid to my local IO target with both send and forget and with a completion routine that allowed me to modify the results before completing the request for mouhid. But from experimenting like this it seems mouhid sends only a single EvtIoRead request down the stack, and, when forwarded to (hidusb.sys) below my filter driver, it just sits on the request until the user moves the mouse, at which point it calls my completion routine. I need to constantly send mouse events to mouhid (scroll events) based on custom data from the mouse, to achieve very smooth scrolling. I am inexperienced with writing drivers, and so as it stands now I don’t know of any other way to send a mouse event back to mouhid except by completing its EvtIoRead requests. What I thought I would do is just save mouhid’s WDFREQUEST in my device context and instead send a WDFREQUEST I created myself down the driver stack to be completed when an actual mouse event occurs from the device. Then I could write to mouhid’s request output buffer and complete the request everytime I need to send a very small delta scroll event (8ms or so), and also copy the output buffer of my own requests sent down the stack to mouhid’s request and complete it when an actual mouse event occurs. The problem with this is when attempting to send a WDFREQUEST I create myself down the stack, my callback routine is immediatley called and the request status is STATUS_PRIVILEGE_NOT_HELD. I was puzzled by this, but stumbled upon a post by Dolon Holan that stated that when sending a request you need a file object. I thought that would be automatically taken care of by called WdfIoTargetFormatRequestForRead, but after checking the local IO target with WdfIoTargetWdmGetTargetFileObject, I found that it returned NULL. So, since Dolon Holan mentioned opening your own IO Target, I did that, and opened a second (remote?) IO target to the file object in mouhid’s request using WdfRequestGetFileObject => WdfFileObjectWdmGetFileObject => WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(&openParams, wdmFile->DeviceObject) and then openParams.TargetFileObject = wdmFile. I then sent my driver created request to this new IO target, and did not get STATUS_PRIVILEGE_NOT_HELD. Instead I got a null pointer access from hidusb.sys and !analyze -v saying IRQL_NOT_LESS_OR_EQUAL (a)… Arg1: 0000000000000000, memory referenced. So I would like to ask, what is the correct way to send your own driver-created WDFREQUEST to your IO target? Any idea why attempting to send my request to the local IO target resulted in STATUS_PRIVILEGE_NOT_HELD? Is there anything that looks wrong in my below code as to why I would get a memory access error? And/or is there any other way to send my own mouse input to mouhid except completing its EvIoRead requests? I am 99% the mouse input report buffer I am creating is the right size. Also in the !analyze -v output it looks like it’s trying to access a null pointer.
// I tried this first -- immediate completion callback with request status STATUS_PRIVILEGE_NOT_HELD
VOID MouseDriverLowerKMDFEvtIoRead(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t Length)
{
WDFDEVICE device;
PDEVICE_EXTENSION deviceContext;
WDF_REQUEST_PARAMETERS params;
NTSTATUS status;
WDFIOTARGET ioTarget;
WDF_REQUEST_SEND_OPTIONS sendOptions;
BOOLEAN sendResult;
size_t readLength;
device = WdfIoQueueGetDevice(Queue);
deviceContext = GetDeviceContext(device);
deviceContext->mouhidReadRequest = Request;
ioTarget = WdfDeviceGetIoTarget(device);
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = device;
WDFREQUEST req;
// I tried this...
//status = WdfRequestCreateFromIrp(&attributes, WdfRequestWdmGetIrp(Request), FALSE, &req);
// And this. Both resulted in an immediate completion callback with request status
// STATUS_PRIVILEGE_NOT_HELD
status = WdfRequestCreate(&attributes, ioTarget, &req);
WDFMEMORY mem;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = req;
status = WdfMemoryCreate(&attributes, NonPagedPool, 0, sizeof(MOUSE_INPUT_REPORT), &mem, NULL);
status = WdfIoTargetFormatRequestForRead(ioTarget, req, mem, NULL, NULL);
WdfRequestSetCompletionRoutine(req, MouseDriverLowerKMDFEvtCompletionRoutine, NULL);
WDF_REQUEST_SEND_OPTIONS sendOp;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOp, 0);
sendResult = WdfRequestSend(req, ioTarget, &sendOp);
}
// I tried this next -- no STATUS_PRIVILEGE_NOT_HELD this time but a null pointer access by hidusb.sys
VOID MouseDriverLowerKMDFEvtIoRead(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t Length)
{
WDFDEVICE device;
PDEVICE_EXTENSION deviceContext;
WDF_REQUEST_PARAMETERS params;
NTSTATUS status;
WDFIOTARGET ioTarget;
WDF_REQUEST_SEND_OPTIONS sendOptions;
BOOLEAN sendResult;
size_t readLength;
device = WdfIoQueueGetDevice(Queue);
deviceContext = GetDeviceContext(device);
deviceContext->mouhidReadRequest = Request;
WDFFILEOBJECT file = WdfRequestGetFileObject(Request);
if (file != NULL && deviceContext->target == NULL)
{
WDF_OBJECT_ATTRIBUTES attributes;
WDF_IO_TARGET_OPEN_PARAMS openParams;
PFILE_OBJECT targetFile = WdfFileObjectWdmGetFileObject(file);
if (targetFile != NULL)
{
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = device;
status = WdfIoTargetCreate(device, &attributes, &ioTarget);
if (NT_SUCCESS(status))
{
WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(&openParams, targetFile->DeviceObject);
openParams.TargetFileObject = targetFile;
status = WdfIoTargetOpen(ioTarget, &openParams);
if (!NT_SUCCESS(status)) { WdfObjectDelete(ioTarget); }
else { deviceContext->target = ioTarget; }
}
}
}
if (deviceContext->target != NULL) ioTarget = deviceContext->target;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = device;
WDFREQUEST req;
// This results in an immediate null pointer access after WdfRequestSend
status = WdfRequestCreate(&attributes, ioTarget, &req);
// This actually does not! But when I delete this temporary request in the completion callback,
// it seems like it deletes mouhid's request output buffer as well because
// WdfRequestRetrieveOutputBuffer returns STATUS_BUFFER_TOO_SMALL, and length of 0.
//status = WdfRequestCreateFromIrp(&attributes, WdfRequestWdmGetIrp(Request), FALSE, &req);
WDFMEMORY mem;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = req;
status = WdfMemoryCreate(&attributes, NonPagedPool, 0, sizeof(MOUSE_INPUT_REPORT), &mem, NULL);
status = WdfIoTargetFormatRequestForRead(ioTarget, req, mem, NULL, NULL);
WdfRequestSetCompletionRoutine(req, MouseDriverLowerKMDFEvtCompletionRoutine, NULL);
WDF_REQUEST_SEND_OPTIONS sendOp;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOp, 0);
sendResult = WdfRequestSend(req, ioTarget, &sendOp);
}
// Last thing I tried was creating my own IRP using this code
PIRP irp;
irp = IoBuildAsynchronousFsdRequest(
IRP_MJ_READ,
device,
&deviceContext->inputReport,
sizeof(MOUSE_INPUT_REPORT),
NULL, // Optional
NULL
);
// Then I created a request from it with code identical to above.
// Doing this still resulted in an immediate memory access error after calling
// WdfRequestSend.
// The completion routine looks like this
status = WdfRequestGetStatus(Request);
device = WdfIoTargetGetDevice(Target);
deviceContext = GetDeviceContext(device);
ioTarget = Target;
report = WdfMemoryGetBuffer(Params->Parameters.Read.Buffer, &readLength);
//WdfObjectDelete(Request); // Seems to delete mouhid's request's buffer as well, possibly because I created my request from its IRP
// STATUS_BUFFER_TOO_SMALL and readLength = 0
status = WdfRequestRetrieveOutputBuffer(deviceContext->mouhidReadRequest, sizeof(MOUSE_INPUT_REPORT), &outReport, &readLength);
// ...
WdfRequestCompleteWithInformation(deviceContext->mouhidReadRequest, STATUS_SUCCESS, sizeof(MOUSE_INPUT_REPORT));
The output of !anaylze -v from the bad memory access is below
IRQL_NOT_LESS_OR_EQUAL (a)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is usually
caused by drivers using improper addresses.
If a kernel debugger is available get the stack backtrace.
Arguments:
Arg1: 0000000000000000, memory referenced
Arg2: 0000000000000002, IRQL
Arg3: 0000000000000000, bitfield :
bit 0 : value 0 = read operation, 1 = write operation
bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status)
Arg4: fffff8027a426312, address which referenced memory
...
BUGCHECK_CODE: a
BUGCHECK_P1: 0
BUGCHECK_P2: 2
BUGCHECK_P3: 0
BUGCHECK_P4: fffff8027a426312
READ_ADDRESS: 0000000000000000
PROCESS_NAME: csrss.exe
IRP_ADDRESS: ffffae0c9b5b2a20
DEVICE_OBJECT: ffffae0c9b8e6060
DRIVER_OBJECT: ffffae0c9af04e30
IMAGE_NAME: hidusb.sys
MODULE_NAME: hidusb
FAULTING_MODULE: fffff8029e650000 hidusb
STACK_COMMAND: .cxr; .ecxr ; kb
FAILURE_BUCKET_ID: AV_IMAGE_hidusb.sys
OS_VERSION: 10.0.19041.1
BUILDLAB_STR: vb_release
OSPLATFORM_TYPE: x64
OSNAME: Windows 10
FAILURE_ID_HASH: {6323539b-739e-4b0b-6e07-ed3d6d6093f3}
Any idea what am I doing wrong and what is the right way to send a personally created WDFREQUEST down the stack? Appreciate any guidance and thanks.