Problems with Cleanup4CtlDeviceRegistered SDV rule

I’m unable to get past the SDV Cleanup4CtlDeviceRegistered rule for my KMDF keyboard driver. Link here: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/kmdf-cleanup4ctldeviceregistered

In a nutshell, “If a PnP driver creates a control device object, it must register for the Cleanup callback.”

The details in the SDV error page say this:

“EvtCleanupCallback or EvtDestroyCallback (in WDF_OBJECT_ATTRIBUTES structure for the Control Device)
or EvtDeviceSelfManagedIoCleanup (in WDF_PNPPOWER_EVENT_CALLBACKS structure) must be registered for
the PnP driver
that creates the Control Device.”

I have tried all of this individually, and in combination, plus additional things it does not specify. It doesn’t seem to matter. The following is a pared down flow of what happens in AddDevice. I set up both the cleanup callbacks and create the device. Then, if it’s the first device being added, I start the process of creating the control device. SDV fails on the call to WdfControlDeviceInitAllocate(). This seems odd for a couple of reasons.

The detailed description above indicates that you need a SelfManagedIoCleanup callback for the “driver” (seems like a typo, you don’t register that callback on a per-driver basis, you register it on a per-device basis, right?) OR you need to register the Cleanup or Destroy callbacks for the control device. But on that second point, if SDV is throwing the error on my call to WdfControlDeviceInitAllocate(), how does it know what I’m going to do when I create the control device? I haven’t gotten to that WdfDeviceCreate() call yet. And in fact, I registered for Cleanup and Destroy callbacks on that device too just to double check, still errors out. I even register for the cleanup callback on the driver as well . To summarize, every place I call WdfDriverCreate or WdfDeviceCreate I am passing a WDF_OBJECT_ATTIRUBTES with an EvtCleanupCallback set. Still fails.

Has anybody gotten this test to work with a PNP KMDF driver which creates a control device? And if so, any idea what I’m missing? Seems like I must be completely misunderstanding what it says the error is or what the criteria are. Or, there is an SDV bug.

        WdfFdoInitSetFilter( DeviceInit);

        WDF_PNPPOWER_EVENT_CALLBACKS_INIT( &wdfPnPCallbacks);

        wdfPnPCallbacks.EvtDeviceSelfManagedIoCleanup = SelfManagedIoCleanup;

        WdfDeviceInitSetPnpPowerEventCallbacks( DeviceInit, &wdfPnPCallbacks);

        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( &wdfAttributes, KBDeviceExt);

        wdfAttributes.EvtCleanupCallback = ObjectCleanup;

        ntStatus = WdfDeviceCreate( 
                                    &DeviceInit, 
                                    &wdfAttributes, 
                                    &hDevice);

        if (InterlockedIncrement16(&DeviceCount) == 1)
        {
                wdfInit = WdfControlDeviceInitAllocate( DriverHandle,
                                                &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);

                WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( &wdfAttributes,
                                                 ControlDeviceExt);

                wdfAttributes.EvtCleanupCallback = CtrlCleanupCallback;

                ntStatus = WdfDeviceCreate( &wdfInit,
                                    &wdfAttributes,
                                    &wdfDevice);

        }

Wow, ok, that was fun.

Hopefully somebody else can learn from my mistake here. Quick background… SDV uses your header files to determine your entry points. And you specifically have to declare them using the Function Roles Types, not the normal C prototype syntax. If you don’t, SDV won’t alert you to that error, but it will just hit any other errors that would come up as if you didn’t have the function in your code at all. The entry points that SDV knows about get written into its SDV-map.h file that you can validate for correctness. This is publicly documented so I can’t really complain too much.

But there’s a big gotcha lurking here. I had my cleanup handler in the header file as:

EVT_WDF_OBJECT_CONTEXT_CLEANUP ObjectCleanup;

This was enough for me to verify that ObjectCleanup was present in the SDV-map.h file and move on to looking for other clues. What I didn’t pick up on is that even though the callback member is declared like this:

PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback;

your function that you set to it must be declared like this:

EVT_WDF_DEVICE_CONTEXT_CLEANUP ObjectCleanup;

SDV apparently uses a text comparison and is not actually type aware.

So even though the two are equivalent:

typedef EVT_WDF_OBJECT_CONTEXT_CLEANUP EVT_WDF_DEVICE_CONTEXT_CLEANUP;

it seems you MUST use the one with “DEVICE” in the name.

The test passes now. Just to stick one last thumb in my eye it lists the result as “not applicable”.

2 Likes

Wow, painful…Out of curiosity, were you also running Code Analysis? That usually does a good job of keeping you honest with your function role types. Not sure if it would catch this one though!

@“Scott_Noone_(OSR)” said:
Wow, painful…Out of curiosity, were you also running Code Analysis? That usually does a good job of keeping you honest with your function role types. Not sure if it would catch this one though!

I was. HLK requires all 3 now - Code Analysis, SDV, and CodeQL… the other 2 were passing without issues but SDV was not having it :wink:

Ugh. Don’t even get me STARTED on CodeQL.

If you’d like some simple tooling to help run CodeQL, we put a quickie script we wrote here on GitHub.

I actually didn’t have any issues with CodeQL once I found out that different versions of the rulesets require different versions of CodeQL!

If you look here it would seem to imply CodeQL 2.6.3 is what is needed for all this stuff. But in reality, if you are testing against WHCP_21H2, you need CodeQL 2.4.6. I have requested that the table be updated…