cwdm

WDM driver StartDeviceWait Rule


I am trying to bring a driver that I inherited into compliance with Microsoft's standards. When I run the Standard Driver Verifier on the source code it says that it fails the StartDeviceWait rule.

The StartDeviceWait rule specifies that the driver should not call KeWaitForSingleObject in the context of start device IRP.

Here is the source code in question:

1: NTSTATUS Cam_Pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
2: {
3:   NTSTATUS             ntStatus        = STATUS_SUCCESS;
4:   PDEVICE_EXTENSION    deviceExtension = DeviceObject->DeviceExtension;
5:   PIO_STACK_LOCATION   IrpSp;
6:   PDEVICE_CAPABILITIES DeviceCapabilities;
7:
8:   IrpSp = IoGetCurrentIrpStackLocation(Irp);
9:   switch (IrpSp->MinorFunction)
10:   {
11:   case IRP_MN_START_DEVICE:
12:     IoMarkIrpPending(Irp);
13:
14:     // Pass down to layer below us first.
15:     ntStatus = Cam_SubmitIrpSynch(DeviceObject, Irp, NULL);
16:     if (NT_SUCCESS(ntStatus))
17:     {
18:       Cam_PnpStartDevice(DeviceObject, Irp);
19:     }
20:
21:     Irp->IoStatus.Status = ntStatus;
22:
23:     IoCompleteRequest(Irp, IO_NO_INCREMENT);
24:     break;
25:   }
26:
27:   return (ntStatus);
28: }
29:
30:
31: NTSTATUS Cam_SubmitIrpSynch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIRB Irb)
32: {
33:   NTSTATUS           ntStatus = STATUS_SUCCESS;
34:   KEVENT             Event;
35:   PIO_STACK_LOCATION NextIrpStack;
36:
37:   if (Irb)
38:   {
39:     NextIrpStack                                           = IoGetNextIrpStackLocation(Irp);
40:     NextIrpStack->MajorFunction                            = IRP_MJ_INTERNAL_DEVICE_CONTROL;
41:     NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
42:     NextIrpStack->Parameters.Others.Argument1              = Irb;
43:   }
44:   else
45:   {
46:     IoCopyCurrentIrpStackLocationToNext(Irp);
47:   }
48:
49:   KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
50:
51:   IoSetCompletionRoutine(Irp, Cam_SynchCompletionRoutine, &Event, TRUE, TRUE, TRUE);
52:
53:   ntStatus = IoCallDriver(DeviceObject, Irp);
54:   if (ntStatus == STATUS_PENDING)
55:   {
56:     // We can only wait for our completion routine if we are less than dispatch level
57:     if (KeGetCurrentIrql() < DISPATCH_LEVEL) 
58:     {
59:       KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);  // This is the line it doesn't like
60:     }
61:   }
62:
63:   ntStatus = Irp->IoStatus.Status;
64:   return (ntStatus);
65: }
66:
67:
68: NTSTATUS Cam_SynchCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT Event)
69: {
70:   if (Event)
71:   {
72:     KeSetEvent(Event, 0, FALSE);
73:   }
74:
75:   return (STATUS_MORE_PROCESSING_REQUIRED);
76: }

According to the Static Driver Verifier:

1: Cam_Pnp
  3: NTSTATUS             ntStatus        = STATUS_SUCCESS;
  4: PDEVICE_EXTENSION    deviceExtension = DeviceObject->DeviceExtension;
  8: sdv_IoGetCurrentIrpStackLocation
  9: switch (IrpSp->MinorFunction)
  12: sdv_IoMarkIrpPending
  15: Cam_SubmitIrpSynch
    33: NTSTATUS ntStatus = STATUS_SUCCESS;
    37: if (Irb)
    46: sdv_IoCopyCurrentIrpStackLocationToNext
    49: KeInitializeEvent
    49: SLIC_KeInitializeEvent_exit
    51: sdv_IoSetCompletionRoutine
    53: sdv_IoCallDriver
    54: if (ntStatus == STATUS_PENDING)
    57: sdv_KeGetCurrentIrql
    57: if (KeGetCurrentIrql() < DISPATCH_LEVEL) 
    59: KeWaitForSingleObject
    59: SLIC_KeWaitForSingleObject_exit
       52: if(($return==STATUS_SUCCESS)&&(irp_event == set_in_completion))
       52: if(($return==STATUS_SUCCESS)&&(irp_event == set_in_completion))
       54: SLIC_ABORT_3_0

If I can't use KeWaitForSingleObject what am I supposed to use in its place. The Microsoft documentation doesn't make it clear. I hope that I have provided enough info, but please let me know if there is anything else that I can provide that will help.


Solution

  • It took a complete rewrite, but I got it to work and pass the test.

    NTSTATUS StartCompletionRoutine(IN PDEVICE_OBJECT nullDeviceObject, IN PIRP Irp, IN PVOID context)
    {
      NTSTATUS       ntStatus     = STATUS_SUCCESS;
      PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)context;
    
      if (NT_SUCCESS(Irp->IoStatus.Status))
      {
        ntStatus             = Cam_PnpStartDevice(DeviceObject, Irp);
        Irp->IoStatus.Status = ntStatus;
      }
     
      return ntStatus;
    }
    
    
    NTSTATUS Cam_Pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
    {
      NTSTATUS           ntStatus        = STATUS_SUCCESS;
      PDEVICE_EXTENSION  deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
      PIO_STACK_LOCATION IrpSp;
    
      IrpSp = IoGetCurrentIrpStackLocation(Irp);
    
      ntStatus = IoAcquireRemoveLock(&deviceExtension->ioRemoveLock, Irp);
      if (ntStatus != STATUS_SUCCESS)
      {
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        goto Exit_Cam_Pnp;
      }
    
      switch (IrpSp->MinorFunction)
      {
      case IRP_MN_START_DEVICE:
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutine(Irp, StartCompletionRoutine, DeviceObject, TRUE, TRUE, TRUE);
    
        IoMarkIrpPending(Irp);
        IoCallDriver(deviceExtension->pStackDeviceObject, Irp);
        ntStatus = STATUS_PENDING;
        break;
      }
    
      IoReleaseRemoveLock(&deviceExtension->ioRemoveLock, Irp);
    
    Exit_Cam_Pnp:
      return (ntStatus);
    }