BAD_POOL_CALLER (c2) bug check when calling WdfWaitLockCreate with driver verifier enabled

I’m testing my KMDF driver with driver verifier and it crashes right away in DriverEntry inside the call to WdfWaitLockCreate like so:

WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &wdfLockDevices);

I’m getting the following bug check:

BAD_POOL_CALLER (c2)
Arg1: 000000000000009b, Attempt to allocate pool with a tag of zero. This would make the pool untrackable and worse, corrupt the existing tag tables.
Arg2: 0000000000000200, Pool type
Arg3: 00000000000000d0, Size of allocation in bytes
Arg4: fffff8027275939e, Caller's address.

I checked it and sure enough the WDF is providing a null tag in Wdf01000!MxMemory::MxAllocatePoolWithTag function. Here's the full stack:

nt!DbgBreakPointWithStatus
nt!KiBugCheckDebugBreak+0x12
nt!KeBugCheck2+0x946
nt!KeBugCheckEx+0x107
nt!VerifierBugCheckIfAppropriate+0xe0
nt!ExAllocatePoolSanityChecks+0x59
nt!VeAllocatePoolWithTagPriority+0x88
nt!VerifierExAllocatePoolWithTag+0x8c
Wdf01000!MxMemory::MxAllocatePoolWithTag+0x11 [minkernel\wdf\framework\shared\inc\primitives\km\MxMemoryKm.h @ 36] 
Wdf01000!FxPoolAllocator+0x6e [minkernel\wdf\framework\shared\object\wdfpool.cpp @ 333] 
Wdf01000!FxPoolAllocateWithTag+0x27 [minkernel\wdf\framework\shared\inc\private\common\FxGlobals.h @ 660] 
Wdf01000!FxObjectHandleAlloc+0xa0 [minkernel\wdf\framework\shared\object\handleapi.cpp @ 232] 
Wdf01000!FxObject::operator new+0x33 [minkernel\wdf\framework\shared\inc\private\common\fxobject.hpp @ 575] 
Wdf01000!FxWaitLock::_Create+0x5b [minkernel\wdf\framework\shared\support\fxwaitlock.cpp @ 45] 
Wdf01000!imp_WdfWaitLockCreate+0x8b [minkernel\wdf\framework\shared\support\fxwaitlockapi.cpp @ 92] 
MyDriver!WdfWaitLockCreate+0x46 [C:\Program Files (x86)\Windows Kits\10\Include\wdf\kmdf\1.15\wdfsync.h @ 141] 
MyDriver!DriverEntry+0x1da [C:\C++\MyDriver\MyDriver\Driver.cpp @ 101] 
MyDriver!FxDriverEntryWorker+0xbf [minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 360] 
MyDriver!FxDriverEntry+0x20 [minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 249] 
nt!PnpCallDriverEntry+0x4c
nt!IopLoadDriver+0x4e5
nt!PipCallDriverAddDeviceQueryRoutine+0x1be
nt!PnpCallDriverQueryServiceHelper+0xda
nt!PipCallDriverAddDevice+0x41c
nt!PipProcessDevNodeTree+0x333
nt!PiProcessStartSystemDevices+0x60
nt!PnpDeviceActionWorker+0x4cc
nt!ExpWorkerThread+0x105
nt!PspSystemThreadStartup+0x55
nt!KiStartSystemThread+0x28

Am I not initializing some globals for WDF, or what am I doing wrong?

PS. I've enabled driver verifier for special pool and pool tracking for the following drivers:

ntoskrnl.exe
Wdf01000.sys
WDFLDR.SYS
WdFilter.sys
ucx01000.sys
MyDriver.sys

Did you call WdfDriverCreate?

Hmm, interesting. Thanks, Tim. So now I'm wondering, where are you supposed to initialize all the WDF globals? Wouldn't doing it after the WdfDriverCreate call create a race condition in their use?

WdfDriverCreate does all of that. That IS the initialization call for KMDF, and it is mandatory. It sets up all of the globals and establishes a lookup table that allows it to map your driver objects to WDF objects.

Thanks, Tim. I was asking about initializing those WDF globals in my own driver.

Also, it's an interesting design on WDF. Somehow if I call its functions before WdfDriverCreate they seem to work and return success, for instance: WdfWaitLockCreate, WdfCollectionCreate. It's only that the driver verifier reveals an issue, also without a clear explanation why. That bug check would be quite confusing for most people to triage too.

What does that sentence even mean? You don’t have access to the WDF internal structures.

This whole discussion seems bizarre to me. The rule is, “every KMDF driver must call WdfDriverCreate before using other KMDF APIs.” End of story. This is not ambiguous. Do that, and your other concerns go away.

That bug check would be quite confusing for most people to triage too.

That bug check will not occur, because most people will have called WdfDriverCreate first, like they are supposed to.

1 Like

The confusion is here: “Wouldn't doing it after the WdfDriverCreate call create a race condition in their use?”

The answer is ‘no, that does not cause a race condition’.

Ok, so @Mark_Roddy , here's an example. What you're saying is that I'm guaranteed that the MyDeviceAdd callback will not start running before DriverEntry returns. Is that correct?

Or, in other words, I am guaranteed that my WdfWaitLockCreate call will execute before MyDeviceAdd.

WDFWAITLOCK GlobalLock;

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status;

    WDF_OBJECT_ATTRIBUTES attrDrv;
    WDF_OBJECT_ATTRIBUTES_INIT(&attrDrv);

    WDF_DRIVER_CONFIG_INIT(&config, MyDeviceAdd);

    status = WdfDriverCreate(DriverObject,
                 RegistryPath,
                 &attrDrv,
                 &config,
                 WDF_NO_HANDLE);

    if(status == STATUS_SUCCESS)
    {
        WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &GlobalLock);
    }

    return status;
}

NTSTATUS MyDeviceAdd(_In_ WDFDRIVER Driver,
    _In_ PWDFDEVICE_INIT DeviceInit)
{
    //...
}

Yes, guaranteed. DriverEntry is called during the “driver load” process, which is separate from the PnP process to add a new device.

It HAS to be that way. Until DriverEntry returns, the I/O subsystem has no idea what your DeviceAdd callback address is.

2 Likes

OK, that explains it. Thanks.