Mining Cryptocurrency...

...is definitely not what happened here, but the results were equally wasteful on the poor i5 CPU 😆

When deploying the driver on a new lab machine something rather unexpected happened: the CPU fans suddenly had a field day and spun up to rather uncomfortable levels noise-wise. I was almost a 100% certain that I've written a gamepad driver, not a fan controller driver so what was going on 🤔 Oh...

14be7173-5b9a-4bb5-b96b-ef31b97c3f9b-image.png

No who's to blame here I wonder...

54818173-f2b1-4fcf-b92f-bab694028551-image.png

Looks like the host process our driver is running under (remember: UMDF means driver logic is just a DLL, loaded by a system process calling kernel APIs in proxy, if necessary). And looking at the DLL list Process Explorer provides one can quickly verify:

76d831c5-b904-4666-9745-b5133b42d06d-image.png

Analysis

First clue that something is not going so well is that now tracing spikes up to over a million captured events of DMF_RequestPassthru in only half a minute! 😲

DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DmfContainerEvtDeviceIoControl] --> Entry DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_ModuleCollectionDeviceIoControl] --> Entry <DmfCollection=0xFFFFFE4AF44978C8 Request=0xFFFFFE4AF4471708 DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] --> Entry <DmfModule=0xFFFFFE4AF44975E8 [ScheduledTask] DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] <-- Exit <DmfModule=0xFFFFFE4AF44975E8 [ScheduledTask] returnValue=0> DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] --> Entry <DmfModule=0xFFFFFE4AF4496128 [DsHidMini] DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] <-- Exit <DmfModule=0xFFFFFE4AF4496128 [DsHidMini] returnValue=0> DmfUModules.Library 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_VirtualHidMini_ModuleDeviceIoControl] --> Entry DmfUModules.Library 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_VirtualHidMini_ModuleDeviceIoControl] <-- Exit <returnValue=0> DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Bridge_ModuleDeviceIoControl] --> Entry DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_Bridge_ModuleDeviceIoControl] <-- Exit <returnValue=0> DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DMF_ModuleCollectionDeviceIoControl] <-- Exit <DmfCollection=0xFFFFFE4AF44978C8 handled=0> DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_INFORMATION [DMF_RequestPassthru]Passthru Request: Request=FFFFFE4AF4471708 DmfUFramework 16980 4112 2021/01/24-12:17:21.522 TRACE_LEVEL_VERBOSE [DmfContainerEvtDeviceIoControl] <-- Exit

So apparently there's a lot of I/O (DeviceIoControl) coming from the upper driver (mshidumdf in this case) hammering our driver, and none of the DMF modules (who use their own default request queue as I haven't defined one) handle the request since it apparently uses an unknown control code. But which one? And why?

Poking under the hood with WinDbg Preview

Time to fire up the trusty (yet somewhat odd named) WinDbg Preview and poke the process. Again, being a user-mode driver allows us to not fiddle with kernel debugging but simply attach to the host process (make sure to use the right one by comparing the PID if there are multiple):

f7c9d289-87c1-4a03-a9bf-8d42fd3c9b61-image.png

Out of curiosity, lets see what the In-Flight Recorder of the framework has to tell us:

!wdfkd.wdflogdump dshidmini.dll -f 206244174: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D578C280 206244175: FxIoQueue::QueueRequest - Queuing WDFREQUEST 0xFFFFFDB92A873718 on WDFQUEUE 0xFFFFFDB92A84AE78 206244176: FxIoQueue::DispatchEvents - Thread 000000000000164C is processing WDFQUEUE 0xFFFFFDB92A84AE78 206244177: FxIoQueue::DispatchRequestToDriver - Calling driver EvtIoDeviceControl for WDFREQUEST 0xFFFFFDB92A873718 206244194: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D578B880 206244195: FxIoQueue::QueueRequest - Queuing WDFREQUEST 0xFFFFFDB92A873E98 on WDFQUEUE 0xFFFFFDB92A84AE78 206244196: FxIoQueue::DispatchEvents - Thread 000000000000164C is processing WDFQUEUE 0xFFFFFDB92A84AE78 206244197: FxIoQueue::DispatchRequestToDriver - Calling driver EvtIoDeviceControl for WDFREQUEST 0xFFFFFDB92A873E98 206244212: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D578CF00 206244213: FxIoQueue::QueueRequest - Queuing WDFREQUEST 0xFFFFFDB92A873998 on WDFQUEUE 0xFFFFFDB92A84AE78 206244214: FxIoQueue::DispatchEvents - Thread 000000000000164C is processing WDFQUEUE 0xFFFFFDB92A84AE78 206244215: FxIoQueue::DispatchRequestToDriver - Calling driver EvtIoDeviceControl for WDFREQUEST 0xFFFFFDB92A873998 206244230: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D578D180 206244231: FxIoQueue::QueueRequest - Queuing WDFREQUEST 0xFFFFFDB92A874398 on WDFQUEUE 0xFFFFFDB92A84AE78 206244232: FxIoQueue::DispatchEvents - Thread 000000000000164C is processing WDFQUEUE 0xFFFFFDB92A84AE78 206244233: FxIoQueue::DispatchRequestToDriver - Calling driver EvtIoDeviceControl for WDFREQUEST 0xFFFFFDB92A874398 206244248: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D57A2330 206244249: FxIoQueue::QueueRequest - Queuing WDFREQUEST 0xFFFFFDB92A85C9E8 on WDFQUEUE 0xFFFFFDB92A84AE78 206244250: FxIoQueue::DispatchEvents - Thread 00000000000028B4 is processing WDFQUEUE 0xFFFFFDB92A84AE78 206244251: FxIoQueue::DispatchRequestToDriver - Calling driver EvtIoDeviceControl for WDFREQUEST 0xFFFFFDB92A85C9E8 206244266: FxPkgIo::Dispatch - WDFDEVICE 0xFFFFFDB92A887238 !devobj 0x00000246D579DB20 0x0000000e(IRP_MJ_DEVICE_CONTROL), IRP_MN 0, IRP 0x00000246D57A2D30

The IRP_MJ_DEVICE_CONTROL corresponds with the observations of the trace log from earlier, so lets take a closer look at the "contents" of the requests and see what they are:

!wdfkd.wdfumirps Number of pending IRPS: 0xc #### CWudfIrp Current Type UniqueId KernelIrp Device Stack ---- ---------------- -------------------------------------------------- ---------------- ---------------- ---------------- 0000 0x246d5769a90 WdfRequestDeviceIoControl 10 0xffffa98a2107b010 0x246d574a870 0001 0x246d5769490 WdfRequestDeviceIoControl 11 0xffffa98a20d4e8a0 0x246d574a870 0002 0x246d57a5a40 WdfRequestDeviceIoControl 0 0x00000000 0x246d578ea90 0003 0x246d57a6440 WdfRequestDeviceIoControl 0 0x00000000 0x246d578ea90 0004 0x246d57a57c0 WdfRequestDeviceIoControl 0 0x00000000 0x246d578ea90 0005 0x246d57a5f40 WdfRequestInternalIoctl 0 0x00000000 0x246d578e0e0 0006 0x246d57a4dc0 WdfRequestInternalIoctl 0 0x00000000 0x246d578e0e0 0007 0x246d57d5100 WdfRequestDeviceIoControl 499c54 0xffffa98a260c4760 0x246d578ea90 0008 0x246d57a89f0 WdfRequestDeviceIoControl 499c55 0xffffa98a25e6b760 0x246d578ea90 0009 0x246d57a7ff0 WdfRequestDeviceIoControl f7f73a 0xffffa98a224ab8a0 0x246d578e0e0 0010 0x246d578c360 WdfRequestDeviceIoControl f7f73c 0xffffa98a25f12b60 0x246d578e0e0 0011 0x246d578cd60 WdfRequestDeviceIoControl f7f771 0xffffa98a20b74bf0 0x246d578e0e0

Only WdfRequestDeviceIoControl are of interest since a UMDF stack can't handle WdfRequestInternalIoctl, by ID alone there ain't much info to go by so just quickly poke at some and take random guesses:

!wdfumirp 0x00000246d57d5100 UM IRP: 0x00000246d57d5100 UniqueId: 0x499c54 Kernel Irp: !irp 0xffffa98a260c4760 Type: WdfRequestDeviceIoControl ClientProcessId: 0x0 Device Stack: !wdfumdevstack 0x00000246d578ea90 IoStatus hrStatus: 0x0 Information: 0x0 Driver/Framework created IRP: No Current Buffers: Input: 0x0000000000000000 / 0 Output: 0x00000246d5767b60 / 0xc IsFrom32BitProcess: No EffectiveIoType: Buffered CancelFlagSet: No Cancel callback: 0x00007ffb86c79750 (WUDFx02000!FxIrpQueue::_WdmCancelRoutineInternal) Overlapped: 0x00000246d57d5228 Total number of stack locations: 2 CurrentStackLocation: StackLocation[ 1 ] StackLocation[ 0 ] UNINITIALIZED > StackLocation[ 1 ] FxDevice: 0x00000246d5741960 dt wudfx02000!FxFileObject 0x0000000000000000 !wdfumfile 0x0000000000000000 Completion: Callback: 0x0000000000000000 Context: 0x0000000000000000 Parameters: (RequestType: WdfRequestDeviceIoControl) Input buffer length: 0x0 Output buffer length: 0xc Control code: 0xb000b

Control code 0xb000blooks familiar and indeed, it's IOCTL_HID_READ_REPORT, the common request for a new input packet. This is expected to be seen and pending as a HID mini-driver issues those in a ping-pong fashion to poll for new information. I'd say we can rule this one out as "perfectly normal". Next:

!wdfumirp 0x00000246d578cd60 UM IRP: 0x00000246d578cd60 UniqueId: 0xf7f771 Kernel Irp: !irp 0xffffa98a20b74bf0 Type: WdfRequestDeviceIoControl ClientProcessId: 0x0 Device Stack: !wdfumdevstack 0x00000246d578e0e0 IoStatus hrStatus: 0x0 Information: 0x0 Driver/Framework created IRP: No Current Buffers not set / not retrieved IsFrom32BitProcess: No EffectiveIoType: Buffered CancelFlagSet: No Cancel callback: (None) Overlapped: 0x00000246d578ce88 Total number of stack locations: 2 CurrentStackLocation: StackLocation[ 1 ] StackLocation[ 0 ] UNINITIALIZED > StackLocation[ 1 ] FxDevice: 0x00000246d5778dc0 dt wudfx02000!FxFileObject 0x0000000000000000 !wdfumfile 0x0000000000000000 Completion: Callback: 0x0000000000000000 Context: 0x0000000000000000 Parameters: (RequestType: WdfRequestDeviceIoControl) Input buffer length: 0x0 Output buffer length: 0x0 Control code: 0xb0233

Oh, control code 0xb0233, who are you, haven't encountered you before! A quick web search reveals... nothing. OK, assuming it's a well known "official" code (which it should be since mshidumdf should only let HID-related requests through to us) we should find info about it in the SDK/WDK headers. But first, dissect it, being lazy I used the OSR tool for it:

b3936f7a-6abe-4598-bfff-b1cabdbb8b0d-image.png

Alrighty, function we're looking for is0x8C or 140 in decimal. The macro HID_CTL_CODE is used to define the HID IOCTLs so a quick grep should bring up the header files to look in:

e2300ed1-1729-4cde-b56c-2ecb5131ba84-image.png

And sure enough; hidclass.h has the answer:

#define IOCTL_HID_DEVICERESET_NOTIFICATION HID_CTL_CODE(140)

Alright, now who are you and what is your deal inside my driver?! Excerpt:

The IOCTL_HID_DEVICERESET_NOTIFICATION request is sent by the HID client driver to HID class driver to wait for a device-initiated reset event. This request can also be sent by the HID Class driver to the HID Minidriver to wait for a device-initiated reset event.

OK, so this request is meant to be kept pending and answered once a device reset has happened, common for USB devices. That explains, why it gets spammed over and over again; if this type of request is completed immediately upon arrival by DMF (since there's nobody left to ask to handle it instead) the upper driver assumes, the event has happened and will issue another request to wait if said event happens again, and again and again and ag... you get the idea 😁 Question remains; why is this suddenly an issue and hasn't appeared at a sooner stage in development? Another little hint from the documentation:

A HID minidriver can enable this feature by adding a registry value in the INF file. The DeviceResetNotificationEnabled under the device's hardware key must be set to 1 to enable the feature.

Soooooo... this has to be explicitly enabled to happen? That's odd, lets look at the appropriate location in the registry:

7cdb74ed-e015-4cc3-bc4d-432240b85132-image.png

Hey hey, look at that, where did you come from, I didn't set ya 🤔 Ah, but the default Windows stock HID drivers did when I first plugged the DS3 into this fresh machine! Searching the input.inf confirms the suspicion:

48cf9937-4f12-4b99-9462-39393bf11599-image.png

So mixing the remains of these default settings with a driver that in fact doesn't handle/supports this request leads to unexpected consequences (no pun intended).

The fix

Armed with this knowledge there's 3 ways to deal with this issue.

Disable registry value by INF

Since the troublesome request won't be issued if the value is disabled, overwriting it in the INF is the first secure step, since most systems in the wild will probably have the registry "poisoned" by the stock driver being in place by default:

[dshidmini.USB_AddReg] HKR,,"LowerFilters",0x00010008,"WUDFRd" ; FLG_ADDREG_TYPE_MULTI_SZ | FLG_ADDREG_APPEND HKR,,"DeviceResetNotificationEnabled",0x00010001,0 ; enabled by default, not supported

Could call it a day now, but there's more, especially to protect against adventurous users turning it back on again 😉

Handle the request with failure

Previously I had no need to define a default queue for my driver since the Virtual HID Mini Module handles all I/O coming from above explicitly. But as we observed, the module doesn't handle IOCTL_HID_DEVICERESET_NOTIFICATION so we need to do that instead:

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); queueConfig.PowerManaged = WdfTrue; queueConfig.EvtIoDeviceControl = DSHM_EvtWdfIoQueueIoDeviceControl; DMF_DmfDeviceInitHookQueueConfig(dmfDeviceInit, &queueConfig); ntStatus = WdfIoQueueCreate( device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue );

The callback:

void DSHM_EvtWdfIoQueueIoDeviceControl( WDFQUEUE Queue, WDFREQUEST Request, size_t OutputBufferLength, size_t InputBufferLength, ULONG IoControlCode ) { NTSTATUS status = STATUS_NOT_IMPLEMENTED; UNREFERENCED_PARAMETER(Queue); UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); FuncEntry(TRACE_DEVICE); switch (IoControlCode) { case IOCTL_HID_DEVICERESET_NOTIFICATION: TraceVerbose( TRACE_DEVICE, "IOCTL_HID_DEVICERESET_NOTIFICATION not supported" ); status = STATUS_NOT_SUPPORTED; break; default: TraceVerbose(TRACE_DEVICE, "Unhandled I/O control code 0x%X", IoControlCode); break; } FuncExitNoReturn(TRACE_DEVICE); WdfRequestComplete(Request, status); }

Good, now even if it comes along again, failure is reported and the upper driver will not issue it again (this has been confirmed by testing). Yay!

DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DmfContainerEvtDeviceIoControl] --> Entry DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_ModuleCollectionDeviceIoControl] --> Entry <DmfCollection=0xFFFFFE50E9098998 Request=0xFFFFFE50E90827C8 DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] --> Entry <DmfModule=0xFFFFFE50E9098708 [ScheduledTask] DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] <-- Exit <DmfModule=0xFFFFFE50E9098708 [ScheduledTask] returnValue=0> DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] --> Entry <DmfModule=0xFFFFFE50E9095DE8 [DsHidMini] DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Generic_ModuleDeviceIoControl] <-- Exit <DmfModule=0xFFFFFE50E9095DE8 [DsHidMini] returnValue=0> DmfUModules.Library 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_VirtualHidMini_ModuleDeviceIoControl] --> Entry DmfUModules.Library 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_INFORMATION [DMF_VirtualHidMini_ModuleDeviceIoControl]IoControlCode = 0xB0233 DmfUModules.Library 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_VirtualHidMini_ModuleDeviceIoControl] <-- Exit <returnValue=0> DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Bridge_ModuleDeviceIoControl] --> Entry sys 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DSHM_EvtWdfIoQueueIoDeviceControl] --> Entry sys 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DSHM_EvtWdfIoQueueIoDeviceControl]IOCTL_HID_DEVICERESET_NOTIFICATION not supported sys 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DSHM_EvtWdfIoQueueIoDeviceControl] <-- DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_Bridge_ModuleDeviceIoControl] <-- Exit <returnValue=1> DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DMF_ModuleCollectionDeviceIoControl] <-- Exit <DmfCollection=0xFFFFFE50E9098998 handled=1> DmfUFramework 14828 13944 2021/01/25-14:29:56.657 TRACE_LEVEL_VERBOSE [DmfContainerEvtDeviceIoControl] <-- Exit Handle the request properly

There's another option I skipped on for now: actually keep the request pending and complete it if e.g. a pipe failure requiring a device reset has happened. I haven't encountered this case in many iterations of testing now so I'll put it on the back seat for now (only to come back later and bite me another day I'm sure 😁 )

There we have it, another mystery solved! 🎉