duplicate WDFREQUEST yields BSOD

Hi everyone,
I am trying to modify the data my KMDF driver gets before it sends it up during a WDFREQUEST read.
After reading this https://community.osr.com/discussion/273945/replacing-wdfmemory-and-or-buffer-in-write-callback and this https://community.osr.com/discussion/216562, I decided to create a new request.
This is what I do :

In the FilterEvtIoRead function :

  1. Create a new request
    → WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, target, &newRequest);

  2. Create new WDFMEMORY
    → WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, NonPagedPool, ‘MyPi’, Length, &newMemory, &newBuffer);

  3. Get the request memory and buffer
    → WdfRequestRetrieveOutputMemory(Request, &outputMemory);
    → outputBuffer = WdfMemoryGetBuffer(outputMemory, NULL);

  4. Copy the data
    → WdfMemoryCopyFromBuffer(newMemory, 0, outputBuffer, Length);

  5. Format the new request with the new memory
    → WdfIoTargetFormatRequestForRead(target, newRequest, newMemory, NULL, NULL);

  6. Set completion routine and send the new request
    → WdfRequestSetCompletionRoutine(newRequest, FilterEvtIoReadCompletionRoutine, filterContext);
    ->WdfRequestSend(newRequest, target, WDF_NO_SEND_OPTIONS);

But this yields me a BSOD… Has anyone any idea of why ?

Thanks for your help,
Axel.

It yields you a BSOD where? There are a lot of places this could go wrong. What type of device is this? What did you do with the old request after this code finished? Did you attach the WDFMEMORY to the request so it is automatically deleted later?

It is a filter driver for serial port.
Actually I am quite new to windows driver developping, and learning is not made very easy with documentation I must say…
I don’t do anything with the old request and the old memory.
What would I need to do with them ? Is that the problem ?

The debugger tells me that :
WDF_VIOLATION (10d)
The Kernel-Mode Driver Framework was notified that Windows detected an error
in a framework-based driver. In general, the dump file will yield additional
information about the driver that caused this bug check.
Arguments:
Arg1: 00000007, A driver attempted to delete a framework object incorrectly
by calling WdfObjectDereference to delete a handle instead
of calling WdfObjectDelete.
Arg2: 79a38a60, Reserved.
Arg3: 865c7598, Reserved.
Arg4: 85eb6a40, Reserved

Thank you a lot for your time

You are not BSODing on the memory copy. Post your code that creates the WDFMEMORY and then deletes it (or where you are dereferencing it). The text is error text is pretty specific here. You are over de-referencing the object. This is causing it to be deleted. WDF objects require explicit deletion. That happens with an API call (WdfObjectDelete) or assigning a parent WDF object that controls the lifetime of the object for you.

Here is my code :

In the read callback :

        // 1) Create the new request
        status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, target, &newRequest);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("WdfRequestCreate failed, status = 0x%08lX\n", status);
            WdfRequestComplete(Request, status);
            return;
        }

        // 2) Create newMemory
        status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, NonPagedPool, 'MyPi', Length, &newMemory, &newBuffer);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("WdfMemoryCreate failed, status = 0x%08lX\n", status);
            WdfRequestComplete(Request, status);
            return;
        }

        status = WdfRequestRetrieveOutputMemory(Request, &outputMemory);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("WdfRequestRetrieveOutputMemory failed, status = 0x%08lX\n", status);
            WdfRequestComplete(Request, status);
            return;
        }

        outputBuffer = WdfMemoryGetBuffer(outputMemory, NULL);
        
        WdfMemoryCopyFromBuffer(newMemory, 0, outputBuffer, Length);

        status = WdfIoTargetFormatRequestForRead(target, newRequest, newMemory, NULL, NULL);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("WdfIoTargetFormatRequestForRead failed, status = 0x%08lX\n", status);
            WdfRequestComplete(Request, status);
            return;
        }
        filterContext->OldRequest=Request;
        filterContext->NewMemory=newMemory;
        WdfRequestSetCompletionRoutine(newRequest, PortSnifferFilterEvtIoReadCompletionRoutine, filterContext);
        sendResult = WdfRequestSend(newRequest, target, WDF_NO_SEND_OPTIONS);

In the completion routine, i delete the old request and new memory, and complete the new request :

        WdfObjectDelete(filterContext->OldRequest); 
        WdfObjectDelete(filterContext->NewMemory);
        WdfRequestComplete(Request, STATUS_SUCCESS);

You can’t just delete the old request, you have to delete the new request (which you created) and complete the old request.

I must have misunderstood something then. The request in the completion routine is my new request right ? So why would i have to delete it ?

To give you more details, I logged all the addresses of the objects :

  • The handle wich is causing the crash is the newRequest
  • The request is passed through WdfRequestComplete

What is wrong with what I do ? Do I need to pass the old request thrhough context, and complete it instead of the new request ?

You created it. When you’re finished with it, you have to clean it up, unless you’re going to reuse it. That’s just the rule. Are you calling WdfRequestComplete in your completion routine on the request you created? Because that would be A Very Bad Thing. WdfRequestComplete releases the IRP back to the driver above you, but no one above you has any clue how to handle a request that you created.

Ok, thank you for your quick answer. This is a lot clearer to me now, I begin to understand how requests are handled.
I complete the old request and delete the new one in the completion routine. But the data still don’t get through, although I correctly copy the old data to the new buffer and format the new request with the new memory (see my code above).
Any idea of why the data does not get to the application ?

You replaced the buffer in the request. If you want the caller’s buffer to contain the data, you need to copy it back into the original buffer that came with the request. Then complete the read request with the number of bytes written (WdfRequestCompleteWithInformation).

But taking a step back for a second, why are you replacing the buffer to begin with? You don’t need your own buffer to inspect the read results in the completion routine, you can simply look at the caller’s buffer. What bigger problem are you trying to solve?

The bigger problem I am trying to solve is to modify the data transmitted. I tried to just create a new WDFMEMORY and format the request with it, but it did not work. This is why I created a new request, but I may be over complicating things here…

More precisely, my original try was :

  1. Create new WDF memory :
WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, NonPagedPool, 'MyPi', Length, &newMemory, &newBuffer);
  1. Copy memory to the new buffer :
WdfRequestRetrieveOutputMemory(Request, &outputMemory);
outputBuffer = WdfMemoryGetBuffer(outputMemory, NULL);
WdfMemoryCopyFromBuffer(newMemory, 0, outputBuffer, Length);
// Modify new buffer later on, for now just tryign to get it work as it is
  1. Format requet with the new memory :
WdfIoTargetFormatRequestForRead(target, Request, newMemory, NULL, NULL);
  1. Register completion routine and send the request
WdfRequestSetCompletionRoutine(Request, PortSnifferFilterEvtIoReadCompletionRoutine, filterContext);
sendResult = WdfRequestSend(Request, target, WDF_NO_SEND_OPTIONS);
  1. In the completion routine, just complete the request :
WdfRequestComplete(Request, STATUS_SUCCESS);

Was it a good way to go, or do I have to stick with creating a whole new request ?

I am confused, maybe because when I see the phrase “modify the data transmitted” I interpret transmit as host → device. Transmit host->device would be a write operation (WdfIoTargetFormatRequestForWrite), but you are formatting the request for a read (WdfIoTargetFormatRequestForRead). So when you say modify the data transmitted, which direction do you mean?

Regardless of transmit direction, if you are not using a larger buffer than what the caller provided you can modify the caller’s buffer in place at the correct point of request processing and not add the complexity of allocating your own buffer and copying data back and forth. For a write operation, you would modify the buffer in your WDFQUEUE dispatch routine before you send the request down the stack. For a read operation, you would set a completion routine and modify the buffer in the completion routine as the request is making its way back up the stack to be completed to the caller.

Sorry, I was not clear, I meant modify data from device to host, this is why I work on read requests.
I will have to use a larger buffer than the original one, to add more data, this is why I created a new buffer.

I tried to put a WdfMemoryCopyFromBuffer in the completion routine, to copy data from the old memory buffer to the new one, but it did not work either.

I read a lot of confusing documentation during the past few days trying to solve my issue, tell me if I get it right :

  • The host emits a read request, going down the stack. The EVT_WDF_IO_QUEUE_IO_READ function of my driver takes the read request, allocates a new memory for it (newMemory and newBuffer), copy the data of the request with WdfRequestRetrieveOutputMemory(Request, &newMemory), and sends it down with that new memory attached (with FormatForRead and SendRequest).
  • The device writes the data to the newBuffer, and the request starts its way up to the host, through the stack
  • The completion routine of the driver is called, so I copy the data from the newBuffer to the old buffer (eventually modify it), deletes the newmemory and complete the request with WdfRequestComplete.
  • The host reads the old buffer

I know I am wrong because the old buffer is too small for the new data to fit, but I don’t know where I am wrong…

To put you in the context, I am trying to develop a driver that adds a signature to the data, for a security reason.

Thank you again for your time, you are a very big help for me.

OK, hang on.

  1. If this is a read request, then there’s nothing in the “output” buffer when you receive the IRP. It is silly for you to copy anything on the way down.
  2. You CANNOT return more data to the user app than the buffer they provided. There is no way to make that work. If the user tried to read 200 bytes, and you want to return 210 bytes, you are screwed. That’s simply impossible. Now, if the user tries to read 200 bytes, but the driver below only returned 190, you can certainly add 10 bytes to the buffer.
  3. Since you cannot return more data to the user than they asked for, there’s really no point in making your own request with your own buffer. Just forward the request you got, and catch it in the completion routine. As long as there’s room, you can add your signature. If there isn’t room, then you cannot achieve your goal.

Ok I get it now, I over complicated things a lot. To modify the data on the flight I just had to modify the buffer in the completion routine…
Again, thank you a lot for your time,
Axel