Exact definition of `WdfRequestSetInformation`?

Hi everyone, noob question, could someone please elucidate the exact semantics and definition of the WdfRequestSetInformation function?

I recently discovered by accident, that the reason my EvtIoDeviceControl output buffer was not being copied back into userspace was due to a missing call to WdfRequestSetInformation right before calling WdfRequestComplete.

However, after looking at MSDN I only find very vague explanations as to what this function does - According to MSDN, “The WdfRequestSetInformation method sets completion status information for a specified I/O request.” Ok, but what information? And for what kinds of I/O requests?

The prototype is:

void WdfRequestSetInformation(
  WDFREQUEST Request,
  ULONG_PTR  Information
);

ULONG_PTR reads to me as “pointer to some Request Information data structure” (or does it mean “a ULONG such that it is guaranteed to be the same type as a pointer type? But why is this guarantee needed here?”), but here we’re using it as a “number of bytes transferred” parameter.

Furthermore, if we read on, we find the Parameter definition:

Information

[in] Driver-defined completion status information for the request.

Driver-defined??? So each driver defines their own semantic to this function? I certainly did not, and yet it seems to have done something very well-defined and critically important!

Sorry if this comes off as a rant, but I’m really frustrated that I can’t seem to find a concrete definition of exactly what that second “Information” parameter means, and in what calling contexts.

It seems everyone “secretely” knows its a byte-count parameter, but reading the docs I find I’m going in circles…

Your rant is not unjustified. Literally, it is a “driver-defined completion status”, but there are many layers that do define a very specific meaning to that field. When you do ReadFile or WriteFile or DeviceIoControl, the value that gets returned in the next-to-the-last parameter is exactly the information field from the IRP. When you use buffered I/O, that field determines how much data is copied from the system buffer back to the user’s buffer.

There are kernel-to-kernel communications where the field has other uses, but for user/kernel interactions, you should always think of it as “the number of bytes I processed”.

Hi Tim,

Thanks for confirming I wasn’t going crazy - I can be a bit more at peace regarding its ambiguity. I’ll take note to call it for I/O buffer transfers…

I thought I’d try stepping through to see if I could wrestle out the complete rules for how it works from the disassembly - I found some interesting observations, especially regarding the Request pointer - it appears to have all of its bits logically negated until the very moment it gets used!

My WdfRequestSetInformation call happens from this kind of context:

VOID
kmdfHelloWorldEvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
    )
{

/*
<switch case statement here based on the `Function` field of the IoControlCode>
*/

        WdfRequestSetInformation(Request, 4); /* <- Put breakpoint on here */
        WdfRequestComplete(Request, STATUS_SUCCESS);
        return;
    }
}

If I stop in this function (Right on the WdfRequestSetInformation call for instance), Request is reported as:

kd> ?? Request
struct WDFREQUEST__ * 0x00004370`00a02f98
   +0x000 unused           : ??

Notice the address.

But then I step into the assembly of imp_WdfRequestSetInformation:

Wdf01000!imp_WdfRequestSetInformation:
fffff805`30c23040 4056            push    rsi
fffff805`30c23042 4883ec30        sub     rsp,30h
fffff805`30c23046 498bf0          mov     rsi,r8 ; r8 = "Information", copy r8 to rsi
fffff805`30c23049 4885d2          test    rdx,rdx ; rdx = "Request", check not NULL
fffff805`30c2304c 0f8402aa0100    je      Wdf01000!imp_WdfRequestSetInformation+0x1aa14 (fffff805`30c3da54) [br=0]

Straightforward enough, setup stack frame, r8 and rsi have 4 (number of bytes), and rdx has the Request “pointer” (although it isn’t really!)

fffff805`30c23052 48895c2440      mov     qword ptr [rsp+40h],rbx ; save rbx
fffff805`30c23057 33c9            xor     ecx,ecx
fffff805`30c23059 488bda          mov     rbx,rdx ; copy "Request" into rbx, OK...
fffff805`30c2305c 48897c2450      mov     qword ptr [rsp+50h],rdi
fffff805`30c23061 48f7d3          not     rbx ; Invert the Request pointer???
fffff805`30c23064 4883e3f8        and     rbx,0FFFFFFFFFFFFFFF8h ; Align it??

We invert, and then align the inverted pointer…

fffff805`30c23068 f6c201          test    dl,1
fffff805`30c2306b 7540            jne     Wdf01000!imp_WdfRequestSetInformation+0x6d (fffff805`30c230ad)

rdx still has the original non-inverted address, and we’re checking its a one… if it is, it implies that the “real” pointer (the inverted one) is atleast even aligned, so we’re checking for alignment?

EDIT: My bad, its the other way around - the original non-inverted pointer LSB is 0, so the inverted will be mis-aligned - but ofcourse, the previous mask takes care of it.

In which case, is this some kind of tagging system?

fffff805`30c2306d b808100000      mov     eax,1008h
fffff805`30c23072 66394308        cmp     word ptr [rbx+8],ax ; Start dereferencing it!
fffff805`30c23076 0f85f2a90100    jne     Wdf01000!imp_WdfRequestSetInformation+0x1aa2e (fffff805`30c3da6e)

Well, from here on it checks (I presume) the IRP packet is of the correct type in this context, and once it verifies that is the case, it puts it in the right place in the structure and leaves… more of that below too.

fffff805`30c2307c 48895c2458      mov     qword ptr [rsp+58h],rbx
fffff805`30c23081 488b7b10        mov     rdi,qword ptr [rbx+10h]
fffff805`30c23085 80bf4101000000  cmp     byte ptr [rdi+141h],0
fffff805`30c2308c 0f85fea90100    jne     Wdf01000!imp_WdfRequestSetInformation+0x1aa50 (fffff805`30c3da90)
fffff805`30c23092 488b8398000000  mov     rax,qword ptr [rbx+98h]
fffff805`30c23099 48897038        mov     qword ptr [rax+38h],rsi
fffff805`30c2309d 488b5c2440      mov     rbx,qword ptr [rsp+40h]
fffff805`30c230a2 488b7c2450      mov     rdi,qword ptr [rsp+50h]
fffff805`30c230a7 4883c430        add     rsp,30h
fffff805`30c230ab 5e              pop     rsi
fffff805`30c230ac c3              ret

So, is this a technique to prevent users of WDF even attempting to dereference any pointer to the opaque WDFREQUEST structure? I.e. WDFREQUEST structures are always allocated at high addresses, so simply inverting them will make that address garbage, but technically in low address, where we can catch that access?

All of the WDFXxx types are opaque handles to the consumer. The implementation of the handle is opaque and an internal implementation detail (for instance early prototypes used real handle tables). Handles are not pointers and not meant to be deferenced directly. You don’t need to disassemble, the source for the API is here . And the magic of decoding a handle to the underlying is FxObjectHandleGetPtr is here

How WDF decodes a handle has nothing to do with the meaning of the Information field in the request. As Tim said, for user mode originated requests, the Information field will be the number of “successful” bytes in the operation. For driver to driver communication, the contents of Information is part of the receiving driver’s IO contract and can be anything (number of bytes, a pointer, a bit field, etc)

I see, thanks Doron_Holan for those links, I really should have looked earlier - It dawned on me later that “Isn’t WDF open source?”, but I lazily used the github browser search rather than just cloning the thing and just grepping for it…

(to be fair, I assumed that the “open source” parts were only a shim layer, and the meat of it would still be hidden away, but apparently not so)

Sure enough the test of the lowest bit is a test for the FxHandleFlagIsOffset in src/framework/shared/inc/private/common/fxobject.hpp:649, and the not is a consequence of

    static
    FxObject*
    _GetObjectFromHandle(
        __in    WDFOBJECT Handle,
        __inout PWDFOBJECT_OFFSET ObjectOffset
        )
<...>
        //
        // We always apply the mask
        //
        handle = handle ^ FxHandleValueMask;
<...>

Well this is all very interesting, thanks for the pointers everyone, I can stop speculating what’s behind the handles.