KMDF DriverEntry failing

Hello everyone,

I have a problem with a KMDF driver failing to load on some client machines and am hoping to get some guidance on how to explain and fix the issue. Unfortunately I have very limited access to the machines in question so am mostly limited to debugging by code inspection.

Here is the situation:

A kernel driver service is being created by a user mode app installed as a service. To load the driver in question it calls CreateService with SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START and SERVICE_ERROR_NORMAL as the options. I suspect that this CreateService call is failing (I say suspect rather than know because I don’t have a log saying so) based on the following facts:

  1. The registry keys associated with the service have been created but the ‘Type’ value is set to 4, i.e SERVICE_DISABLED. Our user mode app never modifies the service config so something else, presumably the Service Control Manager, has set this value.

  2. We also load a WDM TDI filter driver using the same CreateService call and this succeeds, no problems at all.

  3. I know from code inpection that either the CreateService call or a subsequent call to StartService is what is failing.

I’ve concluded that the DriverEntry function from the problem driver must be returning something other than STATUS_SUCCESS under some conditions and this in turn causes CreateService to fail. Could anybody confirm whether or not this is a reasonable line of thought given the limited evidence available?

I’ve observed this behaviour on 6 out of 64 machines. On the other 58 machines the driver is loading successfully. The 6 machines exhibiting the problem include Win XP 32 bit and Win 7 64 bit which should rule out an OS version specific issue.

I’ll post the DriverEntry code and functions called from there below.

If anybody could provide any feedback on my conclusions above and anything likely to fail in the DriverEntry function it would be hugely appreciated. As I say, my current thinking is that there is a problem in this DriverEntry code which is exposed by some local environments and not others. One initial thought is that the security descriptor passed to WdfControlDeviceInitAllocate may cause the failure in some environments. Is this likely?

Thanks in advance for any help, especially given the limited evidence I’m able to provide here.

Ishan

NTSTATUS DriverEntry(IN OUT PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDFDRIVER hDriver;
PWDFDEVICE_INIT pInit = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
PCONTROL_DEVICE_EXTENSION contextData = NULL;
KdPrint((“Driver built: %s %s\n”, DATE, TIME));

WDF_DRIVER_CONFIG_INIT(
&config,
WDF_NO_EVENT_CALLBACK // This is a non-pnp driver.
);

config.DriverInitFlags |= WdfDriverInitNonPnpDriver;

config.EvtDriverUnload = NonPnpEvtDriverUnload;

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = NonPnpEvtDriverContextCleanup;

attributes.ExecutionLevel = WdfExecutionLevelPassive;

attributes.SynchronizationScope = WdfSynchronizationScopeQueue;

status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
&hDriver);
if (!NT_SUCCESS(status)) {
KdPrint ((“WdfDriverCreate failed with status 0x%x\n”, status));
return status;
}

WPP_INIT_TRACING( DriverObject, RegistryPath );

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“XXXXXXXX”);

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“Driver built: %s %s”, DATE, TIME);

// In order to create a control device, we first need to allocate a
// WDFDEVICE_INIT structure and set all properties.
//
pInit = WdfControlDeviceInitAllocate(
hDriver,
&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R
);

if (pInit == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}

status = NonPnpDeviceAdd(hDriver, pInit);
contextData= ControlGetData(g_Device);
if(contextData) {

contextData->boolValue= FALSE;

status = WdfInitializeQueue(&contextData->queue);

if (status == STATUS_SUCCESS)
{
status = InitializeEventAndLock(&(contextData->event));
}

} else {
KdPrint((“Something went wrong attempting to set default values to device context”));
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}

NTSTATUS InitializeEventAndLock(PRKEVENT* sharedEvent)
{
WDF_OBJECT_ATTRIBUTES attributes;
NTSTATUS success;

if (sharedEvent)
{
*sharedEvent = NULL;
}

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
success = WdfWaitLockCreate(&attributes, &g_eventLock);

return success;
}

NTSTATUS NonPnpDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;

UNREFERENCED_PARAMETER( Driver );

PAGED_CODE();

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“NonPnpDeviceAdd DeviceInit %p\n”, DeviceInit);

// we need to initialise the device context area, we can guarantee this will be global
// in context of our device , the equivalent to the DEVICE_EXTENSION in WDM Drivers
// This provides an accessor method to the context area data.

// Set exclusive to TRUE so that no more than one app can talk to the
// control device at any time.
WdfDeviceInitSetExclusive(DeviceInit, FALSE);

// Set the IO type of the device
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);

status = NonPnpDeviceSetAttributes(&Driver, &DeviceInit);

// If the device is created successfully, framework would clear the
// DeviceInit value. Otherwise device create must have failed so we
// should free the memory ourself.
//
if (DeviceInit != NULL) {
WdfDeviceInitFree(DeviceInit);

}

return status;

}

NTSTATUS NonPnpDeviceSetAttributes (WDFDRIVER* Driver, PWDFDEVICE_INIT* DeviceInit)
{
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_FILEOBJECT_CONFIG fileConfig;
WDFQUEUE queue;
WDFDEVICE controlDevice;
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ;
DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ;
UNREFERENCED_PARAMETER( Driver );

//assign name to the device that we have created
status = WdfDeviceInitAssignName(*DeviceInit, &ntDeviceName);

if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceInitAssignName failed %!STATUS!”, status);
return status;
}

//now set the shut down and notification information for the device
WdfControlDeviceInitSetShutdownNotification(*DeviceInit,
NonPnpShutdown,
WdfDeviceShutdown);

WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
WDF_NO_EVENT_CALLBACK, // not interested in Create IRP
WDF_NO_EVENT_CALLBACK, // not interested in Close IRP
WDF_NO_EVENT_CALLBACK // not interested in cleaning up IRP
);

WdfDeviceInitSetFileObjectConfig(*DeviceInit,
&fileConfig,
WDF_NO_OBJECT_ATTRIBUTES);

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
CONTROL_DEVICE_EXTENSION);

status = WdfDeviceCreate(DeviceInit,
&attributes,
&controlDevice);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceCreate failed %!STATUS!”, status);
return status;
}

status = WdfDeviceCreateSymbolicLink(controlDevice,
&symbolicLinkName);

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceCreateSymbolicLink failed %!STATUS!”, status);
return status;
}

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchSequential);

ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl; //IRP_MJ_DEVICE_CONTROL

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

status = WdfIoQueueCreate(controlDevice,
&ioQueueConfig,
&attributes,
&queue // pointer to default queue
);

if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfIoQueueCreate failed %!STATUS!”, status);
return status;
}

// Control devices must notify WDF when they are done initializing. I/O is
// rejected until this call is made.
WdfControlFinishInitializing(controlDevice);

//now allocate this so we can use it later;
g_Device = controlDevice;

return STATUS_SUCCESS;
}

What version of kmdf? Are you also calling into the kmdf coinstaller to install the kmdf runtime?

d

Bent from my phone


From: xxxxx@baesystemsdetica.commailto:xxxxx
Sent: ?3/?6/?2014 4:49 AM
To: Windows System Software Devs Interest Listmailto:xxxxx
Subject: [ntdev] KMDF DriverEntry failing

Hello everyone,

I have a problem with a KMDF driver failing to load on some client machines and am hoping to get some guidance on how to explain and fix the issue. Unfortunately I have very limited access to the machines in question so am mostly limited to debugging by code inspection.

Here is the situation:

A kernel driver service is being created by a user mode app installed as a service. To load the driver in question it calls CreateService with SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START and SERVICE_ERROR_NORMAL as the options. I suspect that this CreateService call is failing (I say suspect rather than know because I don’t have a log saying so) based on the following facts:

1) The registry keys associated with the service have been created but the ‘Type’ value is set to 4, i.e SERVICE_DISABLED. Our user mode app never modifies the service config so something else, presumably the Service Control Manager, has set this value.

2) We also load a WDM TDI filter driver using the same CreateService call and this succeeds, no problems at all.

3) I know from code inpection that either the CreateService call or a subsequent call to StartService is what is failing.

I’ve concluded that the DriverEntry function from the problem driver must be returning something other than STATUS_SUCCESS under some conditions and this in turn causes CreateService to fail. Could anybody confirm whether or not this is a reasonable line of thought given the limited evidence available?

I’ve observed this behaviour on 6 out of 64 machines. On the other 58 machines the driver is loading successfully. The 6 machines exhibiting the problem include Win XP 32 bit and Win 7 64 bit which should rule out an OS version specific issue.

I’ll post the DriverEntry code and functions called from there below.

If anybody could provide any feedback on my conclusions above and anything likely to fail in the DriverEntry function it would be hugely appreciated. As I say, my current thinking is that there is a problem in this DriverEntry code which is exposed by some local environments and not others. One initial thought is that the security descriptor passed to WdfControlDeviceInitAllocate may cause the failure in some environments. Is this likely?

Thanks in advance for any help, especially given the limited evidence I’m able to provide here.

Ishan

NTSTATUS DriverEntry(IN OUT PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDFDRIVER hDriver;
PWDFDEVICE_INIT pInit = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
PCONTROL_DEVICE_EXTENSION contextData = NULL;
KdPrint((“Driver built: %s %s\n”, DATE , TIME ));

WDF_DRIVER_CONFIG_INIT(
&config,
WDF_NO_EVENT_CALLBACK // This is a non-pnp driver.
);

config.DriverInitFlags |= WdfDriverInitNonPnpDriver;

config.EvtDriverUnload = NonPnpEvtDriverUnload;

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = NonPnpEvtDriverContextCleanup;

attributes.ExecutionLevel = WdfExecutionLevelPassive;

attributes.SynchronizationScope = WdfSynchronizationScopeQueue;

status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
&hDriver);
if (!NT_SUCCESS(status)) {
KdPrint ((“WdfDriverCreate failed with status 0x%x\n”, status));
return status;
}

WPP_INIT_TRACING( DriverObject, RegistryPath );

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“XXXXXXXX”);

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“Driver built: %s %s”, DATE , TIME );

// In order to create a control device, we first need to allocate a
// WDFDEVICE_INIT structure and set all properties.
//
pInit = WdfControlDeviceInitAllocate(
hDriver,
&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R
);

if (pInit == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}

status = NonPnpDeviceAdd(hDriver, pInit);
contextData= ControlGetData(g_Device);
if(contextData) {

contextData->boolValue= FALSE;

status = WdfInitializeQueue(&contextData->queue);

if (status == STATUS_SUCCESS)
{
status = InitializeEventAndLock(&(contextData->event));
}

} else {
KdPrint((“Something went wrong attempting to set default values to device context”));
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}

NTSTATUS InitializeEventAndLock(PRKEVENT* sharedEvent)
{
WDF_OBJECT_ATTRIBUTES attributes;
NTSTATUS success;

if (sharedEvent)
{
sharedEvent = NULL;
}

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
success = WdfWaitLockCreate(&attributes, &g_eventLock);

return success;
}

NTSTATUS NonPnpDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;

UNREFERENCED_PARAMETER( Driver );

PAGED_CODE();

TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
“NonPnpDeviceAdd DeviceInit %p\n”, DeviceInit);

// we need to initialise the device context area, we can guarantee this will be global
// in context of our device , the equivalent to the DEVICE_EXTENSION in WDM Drivers
// This provides an accessor method to the context area data.

// Set exclusive to TRUE so that no more than one app can talk to the
// control device at any time.
WdfDeviceInitSetExclusive(DeviceInit, FALSE);

// Set the IO type of the device
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);

status = NonPnpDeviceSetAttributes(&Driver, &DeviceInit);

// If the device is created successfully, framework would clear the
// DeviceInit value. Otherwise device create must have failed so we
// should free the memory ourself.
//
if (DeviceInit != NULL) {
WdfDeviceInitFree(DeviceInit);

}

return status;

}

NTSTATUS NonPnpDeviceSetAttributes (WDFDRIVER
Driver, PWDFDEVICE_INIT* DeviceInit)
{
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_FILEOBJECT_CONFIG fileConfig;
WDFQUEUE queue;
WDFDEVICE controlDevice;
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ;
DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ;
UNREFERENCED_PARAMETER( Driver );

//assign name to the device that we have created
status = WdfDeviceInitAssignName(*DeviceInit, &ntDeviceName);

if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceInitAssignName failed %!STATUS!”, status);
return status;
}

//now set the shut down and notification information for the device
WdfControlDeviceInitSetShutdownNotification(*DeviceInit,
NonPnpShutdown,
WdfDeviceShutdown);

WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
WDF_NO_EVENT_CALLBACK, // not interested in Create IRP
WDF_NO_EVENT_CALLBACK, // not interested in Close IRP
WDF_NO_EVENT_CALLBACK // not interested in cleaning up IRP
);

WdfDeviceInitSetFileObjectConfig(*DeviceInit,
&fileConfig,
WDF_NO_OBJECT_ATTRIBUTES);

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
CONTROL_DEVICE_EXTENSION);

status = WdfDeviceCreate(DeviceInit,
&attributes,
&controlDevice);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceCreate failed %!STATUS!”, status);
return status;
}

status = WdfDeviceCreateSymbolicLink(controlDevice,
&symbolicLinkName);

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfDeviceCreateSymbolicLink failed %!STATUS!”, status);
return status;
}

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchSequential);

ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl; //IRP_MJ_DEVICE_CONTROL

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

status = WdfIoQueueCreate(controlDevice,
&ioQueueConfig,
&attributes,
&queue // pointer to default queue
);

if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, “WdfIoQueueCreate failed %!STATUS!”, status);
return status;
}

// Control devices must notify WDF when they are done initializing. I/O is
// rejected until this call is made.
WdfControlFinishInitializing(controlDevice);

//now allocate this so we can use it later;
g_Device = controlDevice;

return STATUS_SUCCESS;
}


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer</mailto:xxxxx></mailto:xxxxx>

Thanks for your response Doron. We are using the WdfCoInstaller dll at runtime to call functions such as WdfPre/PostDeviceInstall, the version is WdfCoInstaller01007.dll. The WinDDK version we’re using is 6001.18002.

Update: I’ve managed to get a debug version of the controlling user mode app onto one of the problem machines and it turns out that CreateService is failing because the service already exists and is marked for deletion.

This would indicate that the problem has nothing to do with the DriverEntry code which is a relief but does throw up another question:

Is it possible for a service to get marked for deletion in the database but never actually get deleted? If so any possible explanation as to why? Has anybody seen this behaviour before?

Thanks in advance for any advice

> Is it possible for a service to get marked for deletion in the database but never actually get deleted

Yes, usually, very often, this is normal.

Reboot is the cure.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

Thanks for the advice Maxim. I won’t be able to control the rebooting of machines in practice but at least I understand what is going on now.

If your driver and any companion user-mode processes fail to release all
resources in DriverUnload then you may see the “marked for deletion” status.
Check whether you can reliably unload (stop) and reload (start) your driver.

If your TDI driver is a TDI filter driver, then I hope that you know it
cannot be unloaded reliably. If TDI client, then you should be OK.

Thomas F. Divine

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@baesystemsdetica.com
Sent: Thursday, March 6, 2014 1:14 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF DriverEntry failing

Thanks for the advice Maxim. I won’t be able to control the rebooting of
machines in practice but at least I understand what is going on now.


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Hi Thomas, thanks for the extra information. I will check to make sure we are definitely releasing all resources in DriverUnload. The driver does unload and reload as expected in the tests that we’ve done but perhaps there are some conditions under which this is not the case.

Re the TDI driver, it is a filter but we are not attempting to unload it.

After thinking more about this problem I believe it is caused by the user mode app holding onto the service handle for longer than it should after calling DeleteService - am going to perform some experiments to confirm this today.

Also Doron, to answer your previous question properly the KMDF version is 1.7.

Thanks again for the advice.

Ishan