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:
-
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.
-
We also load a WDM TDI filter driver using the same CreateService call and this succeeds, no problems at all.
-
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;
}