Getting USB descriptors from a class filter

Hi all,

I have few questions related to an USB class filtering.

  • I shouldn’t send URBs from a class filter to a PDO, but why? I mean, if a have the PDO of a USB device, why is not safe to ask for the device descriptor, for example, after the device is started?

  • Is a reasonable approach to wait for URBs sent by the system to complete succesfully and then read the information from them? I guess… with lower filter?

  • Is there any way to get the descriptors from an (upper or lower) USB class filter?

Thanks in advance,

Rui

Who says it’s NOT safe? It sounds perfectly reasonable to me.

Well… “it depends” – I don’t TYPICALLY like *any* synchronous “wait for it to complete” approach, when it’s used generically in the path to a controller. Rather, I’d like to see you send the request asynchronously and get the results in a Completion Callback. All using WDF, of course.

Peter
OSR
@OSRDrivers

xxxxx@protonmail.com wrote:

I have few questions related to an USB class filtering.

I would caution that the term “USB class filtering” can have several
different meanings.  What “class” are you filtering?

  • I shouldn’t send URBs from a class filter to a PDO, but why? I mean,
    if a have the PDO of a USB device, why is not safe to ask for the
    device descriptor, for example, after the device is started? 

It is safe, although typically it’s not necessary, because you can
intercept the device descriptor request from the driver you’re filtering
and snaggle its output.  You will typically need to monitor many of the
standard requests, so you can learn which interfaces are selected, for
example.

  • Is a reasonable approach to wait for URBs sent by the system to
    complete succesfully and then read the information from them? I
    guess… with lower filter?

Absolutely.  I do it all the time.

  • Is there any way to get the descriptors from an (upper or lower) USB
    class filter?

Monitoring as a lower filter is easy.  From an upper filter, you’d need
to send your own request.  Note that, as an upper filter, you won’t know
which interfaces are selected, or which alternate settings are chosen. 
A USB upper filter typically doesn’t deal in URBs very much; you deal in
whatever interface is spoken out the top end of the driver you’re filtering.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Peter, Tim, thank you. Your help is really appreciated.

After reading your answers I realized there are things (even more) I dont understand properly enough to write the filter. I have been reading a bit more… and I have new basic doubts.

  • According to MSDN the USB class includes USB host controllers and USB hubs, but not USB peripherals. What does “but not USB peripherals” really mean? I can see USB enumerated devices (\USB\VID_*)… and monitor the IRPs before and after arriving the FDO. Is it something like not all USB devices are going to be informed in my AddDevice function?

  • The fact that Peter suggests using WDF…implies that I was wrong about something very basic. As I expected to monitor URBs I wrongly thought the filter is considered a bus filter. Ok, so after reading the forum, I guess the next move is to rewrite it using KMDF, isn’t it? Every class filter can be written with KMDF regardless the class?

  • Regardless the framework used, and although this WDM filter is going to the end up in the Recycle Bin… is my code BSODing in (Windows Server 2012, IRQL is PASSIVE_LEVEL) because the approach? It *seemed* to work OK in XP, what I do is:

  1. Install a completion routine on IRP_MN_START_DEVICE
  2. If the start is successful, I try to get the class of the device to save it in my device descriptor by:
    2.1 Declare URB & USB_DEVICE_DESCRIPTOR structs in the stack
    2.2 Init the URB with UsbBuildGetDescriptorRequest
    2.3 Create an IRP with IoBuildDeviceIoControlRequest with an event to wait in case is pended
    2.4 Setup the stack location with the address of the URB
    2.5 Call IoCallDriver with the PDO
    2.6 If needed, wait until the IRP is completed
    2.7 Parse the device descriptor in case of success

Should this approach work? If I move it to WDF, isn’t it this, more or less, what would happen under the hood?

Thank you again.

Rui

On May 26, 2018, at 11:34 AM, xxxxx@protonmail.com wrote:
>
> - According to MSDN the USB class includes USB host controllers and USB hubs, but not USB peripherals. What does “but not USB peripherals” really mean? I can see USB enumerated devices (\USB\VID_*)… and monitor the IRPs before and after arriving the FDO. Is it something like not all USB devices are going to be informed in my AddDevice function?

The situation is not straightforward, in part because the term “class” is heavily overloaded.

We have the device install class, which is the Class and ClassGUID specified in the INF file. In this case, the USB class has a GUID that starts 36fc9e60. This class is intended for USB host controllers and hubs. However, because there was no other convenient place, many people making USB devices put them in Class=USB, just because the name was so tempting. Microsoft added the USBDevice class (which starts 88bae032) in Windows 10 to solve that problem, but there are a lot of old driver packages out there.

Microsoft also uses the term “class” to refer to device interfaces, which are PnP concepts that are managed at run-time, not specified in the INF file. There is a device interface class for USB devices as well, and I believe the hubs create that interface for any subdivide that they create.

A “class filter driver” is ordinarily filtering based on the device install class. That means anything that appears in Device Manager under “Universal Serial Bus Controllers”, which might include devices that are not actually USB controllers.

> - The fact that Peter suggests using WDF…implies that I was wrong about something very basic. As I expected to monitor URBs I wrongly thought the filter is considered a bus filter. Ok, so after reading the forum, I guess the next move is to rewrite it using KMDF, isn’t it? Every class filter can be written with KMDF regardless the class?

No, URBs are sent by individual devices. A straight device filter or class filter can grab those. Being a bus filter is more about watching complicated PnP interactions. If all you need are URBs, that’s a straight filter.

Filter drivers are trivially easy in KMDF. There is NO reason to write a filter driver in straight WDM any more.

> - Regardless the framework used, and although this WDM filter is going to the end up in the Recycle Bin… is my code BSODing in (Windows Server 2012, IRQL is PASSIVE_LEVEL) because the approach? It seemed to work OK in XP, what I do is:
>
> 1. Install a completion routine on IRP_MN_START_DEVICE
> 2. If the start is successful, I try to get the class of the device to save it in my device descriptor by:
> 2.1 Declare URB & USB_DEVICE_DESCRIPTOR structs in the stack
> 2.2 Init the URB with UsbBuildGetDescriptorRequest
> 2.3 Create an IRP with IoBuildDeviceIoControlRequest with an event to wait in case is pended
> 2.4 Setup the stack location with the address of the URB
> 2.5 Call IoCallDriver with the PDO
> 2.6 If needed, wait until the IRP is completed
> 2.7 Parse the device descriptor in case of success
>
> Should this approach work? If I move it to WDF, isn’t it this, more or less, what would happen under the hood?

As long as you are doing this in the completion routine, this should be OK. Where do you get the blue screen?

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thanks a lot for the feedback.

This is a stack trace in Windows XP, which seems reasonable, during a succesful processing of the IRP:

kd> kb
ChildEBP RetAddr Args to Child
bad07680 ba49ff14 45447367 8ac8ef20 bad077f0 USBPORT!USBPORT_SCT_GetSetDescriptor+0x46 <- Get Descriptor makes sense…
bad076ac ba4a5088 89b3c618 89b8d028 000000b0 USBPORT!USBPORT_ProcessURB+0x3f4
bad076cc ba48e3d2 89b3c618 8ac8ef20 89b3c618 USBPORT!USBPORT_PdoInternalDeviceControlIrp+0x7e
bad076f0 804ee119 8ac8efd8 89b3c770 806d22e8 USBPORT!USBPORT_Dispatch+0x148
bad07700 8064d628 8ac8ef20 bad077f0 89c83e38 nt!IopfCallDriver+0x31
bad07724 baa5859c bad0774c baa5c82d 8ac8ef20 nt!IovCallDriver+0xa0
bad0772c baa5c82d 8ac8ef20 89b3c618 8ac8ef20 usbhub!USBH_PassIrp+0x18
bad0774c baa5d0ae 89cdd1d8 8ac8ef20 89c83d80 usbhub!USBH_PdoUrbFilter+0xbd
bad07768 baa5a5e4 bad077f0 8ac8ef20 bad077ac usbhub!USBH_PdoDispatch+0x202
bad07778 804ee119 89c83d80 8ac8ef20 806d22e8 usbhub!USBH_HubDispatch+0x48
bad07788 8064d628 8b022ed8 bad07908 8b022fb4 nt!IopfCallDriver+0x31
bad077ac ba58308d bad07dcc 00040000 00000000 nt!IovCallDriver+0xa0
bad077dc ba583160 89c83d80 bad077f0 89c83d80 MyDriver!UsbSendUrb+0x6d <- Send the URB
bad07850 ba57c262 89c83d80 bad07860 01100112 MyDriver!UsbReadDeviceDescriptor+0x50
bad07880 8064d830 89c82678 8b022ed8 00000000 MyDriver!StartDeviceCompleted+0x202 <- Completion routine, sending URB… <<<<<<
bad078a4 804f069e 89c82678 8b022ed8 bad07908 nt!IovpLocalCompletionRoutine+0xb4
bad078d4 8064dcb8 8996ae90 89c84030 8b022e00 nt!IopfCompleteRequest+0xa2
bad07940 babce03f ffffffff ffffffff 00000000 nt!IovCompleteRequest+0x9a
bad07988 babc85e5 89c840e8 8b022ed8 0000001b usbccgp!USBC_PnP+0x29f
bad079c4 804ee119 89c84030 8b022ed8 806d22e8 usbccgp!USBC_Dispatch+0x195
bad079d4 8064d628 89c82678 89d884d0 8b022e00 nt!IopfCallDriver+0x31
bad079f8 ba57c459 89c82730 00000000 8b022fd8 nt!IovCallDriver+0xa0
bad07a0c 804ee119 89c82678 8b022ed8 806d22e8 MyDriver!DispatchPnP+0x139 <- IRP_MN_START_DEVICE recieved, set the completion routine <<<<<
bad07a1c 8064d628 8b022ffc bad07aac 8b022ed8 nt!IopfCallDriver+0x31
bad07a40 80587fc9 bad07aac 89c83d80 00000000 nt!IovCallDriver+0xa0
bad07a6c 80588047 89c82678 bad07a88 00000000 nt!IopSynchronousCall+0xb7
bad07ab0 804f514c 89c83d80 89c164e0 00000001 nt!IopStartDevice+0x4d
bad07acc 805876f7 89c83d80 89c16401 00000000 nt!PipProcessStartPhase1+0x4e
bad07d24 80587bca 89d8fee8 00000001 00000000 nt!PipProcessDevNodeTree+0x1db
bad07d4c 804f58d6 00000003 80552040 8055b0fc nt!PiProcessStartSystemDevices+0x3a
bad07d74 80534c02 00000000 00000000 89da1da8 nt!PipDeviceActionWorker+0x166
bad07dac 805c6160 00000000 00000000 00000000 nt!ExpWorkerThread+0x100
bad07ddc 80541dd2 80534b02 00000001 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

##################################################################################

Now the crash in Windows Server 2012:

To test the filter, I attach to every device in AddDevice() and then I try to do the same. The first devices that appear are root hubs: USB\ROOT_HUB, USB\ROOT_HUB20 and USB\ROOT_HUB30 which I suspect correspond to UHCI, EHCI and xHCI. For the first two root hubs, the IoCallDriver() call fails with STATUS_NO_SUCH_DEVICE which maybe make sense for root hubs, I don’t know yet, but USBview is showing device descriptors only for generic hubs connected to root hubs, but not for root hubs. Anyways the third one does not return an error, it just crashes with bugcheck KERNEL_MODE_EXCEPTION_NOT_HANDLED.

kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: ffffffffc0000005, The exception code that was not handled
Arg2: fffff8000040f066, The address that the exception occurred at
Arg3: ffffd0002078c488, Exception Record Address
Arg4: ffffd0002078bc90, Context Record Address

Debugging Details:

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

FAULTING_IP:
Wdf01000!imp_WdfObjectGetTypedContextWorker+26
fffff800`0040f066 4c8b5010 mov r10,qword ptr [rax+10h]

EXCEPTION_RECORD: ffffd0002078c488 – (.exr 0xffffd0002078c488)
ExceptionAddress: fffff8000040f066 (Wdf01000!imp_WdfObjectGetTypedContextWorker+0x0000000000000026)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 0000000000000000
Parameter[1]: 0000307ffee7b438
Attempt to read from address 0000307ffee7b438

CONTEXT: ffffd0002078bc90 – (.cxr 0xffffd0002078bc90)
rax=0000307ffee7b428 rbx=0000000000000000 rcx=0000000000000000
rdx=ffffcf8001184bd0 rsi=0000000000000000 rdi=ffffd0002078ca40
rip=fffff8000040f066 rsp=ffffd0002078c6c0 rbp=000000000000000b
r8=fffff80001bb00b0 r9=fffff801ea1156b0 r10=ffffe0000518fc70
r11=0000000000000000 r12=0000000000000021 r13=0000000000000000
r14=00001ffffae70858 r15=ffffcf800111ed30
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010246
Wdf01000!imp_WdfObjectGetTypedContextWorker+0x26:
fffff8000040f066 4c8b5010 mov r10,qword ptr [rax+10h] ds:002b:0000307ffee7b438=???
Resetting default scope

DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT

PROCESS_NAME: System

CURRENT_IRQL: 0

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_PARAMETER1: 0000000000000000

EXCEPTION_PARAMETER2: 0000307ffee7b438

READ_ADDRESS: 0000307ffee7b438

FOLLOWUP_IP:
ucx01000!Urb_USBPORTStyle_ProcessURB+98
fffff800`01ba3a84 4533db xor r11d,r11d

BUGCHECK_STR: 0x7E

LAST_CONTROL_TRANSFER: from fffff80001ba3a84 to fffff8000040f066

STACK_TEXT:
ffffd0002078c6c0 fffff80001ba3a84 : 0000000000000000 fffff801ea473de9 ffffe00000e6b330 fffff801ea472368 : Wdf01000!imp_WdfObjectGetTypedContextWorker+0x26
ffffd0002078c710 fffff80001b99e20 : 00001ffffae70858 ffffcf800111ed30 ffffd0002078c7d0 ffffcf800111ef68 : ucx01000!Urb_USBPORTStyle_ProcessURB+0x98
ffffd0002078c770 fffff80000414d43 : ffffcf800111ed30 ffffe00000341818 000000000000000f ffffe0000518f7a0 : ucx01000!RootHub_Pdo_EvtInternalDeviceControlIrpPreprocessCallback+0x448 <<<< Maybe this calls to the preprocess callbacks I have been reading about (WDF). I guess makes sense because URB is sent via IRP_MJ_INTERNAL_DEVICE_CONTROL
ffffd0002078c800 fffff801ea465911 : ffffe00000e6bed0 0000000000000002 fffff801e9f1c04c ffffe00005190710 : Wdf01000!FxDevice::DispatchWithLock+0xb01
ffffd0002078c8e0 fffff8000031a989 : ffffcf800111ed30 fffff80001071def fffff801e9f1c04c ffffe00000e6be30 : nt!IovCallDriver+0x3cd
ffffd0002078c930 fffff80001071def : fffff801ea0ae880 ffffe00005433040 ffffe0000544ae50 0000000000000000 : VerifierExt!IofCallDriver_internal_wrapper+0x71
ffffd0002078c970 fffff80001071f18 : ffffe00005190710 ffffd0002078ca40 0000000000000000 fffff801ea0ae890 : MyDriver!UsbSendUrb+0xaf
ffffd0002078ca20 fffff8000106949d : ffffe00005190710 ffffd0002078cb48 ffffe0000544ae50 ffffe00000e81010 : MyDriver!UsbReadDeviceDescriptor+0x78
ffffd0002078cb00 fffff801ea4726a6 : ffffe00005433d20 ffffcf8001184bd0 0000000000000000 fffff801e9e02000 : MyDriver!StartDeviceCompleted+0x25d <- Completion routine, sending URB… <<<<<<<<<<
ffffd0002078cb70 fffff801ea4729e4 : fffff801ea0ae890 fffff801ea0ae880 0000000000000080 ffffe000001e4040 : nt!ViPendingCompleteAfterWait+0xda
ffffd0002078cbc0 fffff801e9ee9664 : 0072a845c7202444 740069ac45c70074 006f0069b045c700 c70000006eb445c7 : nt!ViPendingWorkerThread+0x2c
ffffd0002078cc00 fffff801e9f586c6 : fffff801ea0f3180 ffffe000001e4040 ffffe000001e3880 000001b941402454 : nt!PspSystemThreadStartup+0x58
ffffd0002078cc60 0000000000000000 : ffffd0002078d000 ffffd00020787000 0000000000000000 0000000000000000 : nt!KxStartSystemThread+0x16

SYMBOL_STACK_INDEX: 1

SYMBOL_NAME: ucx01000!Urb_USBPORTStyle_ProcessURB+98

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: ucx01000

IMAGE_NAME: ucx01000.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 5215f7fc

STACK_COMMAND: .cxr 0xffffd0002078bc90 ; kb

FAILURE_BUCKET_ID: X64_0x7E_VRF_ucx01000!Urb_USBPORTStyle_ProcessURB+98

BUCKET_ID: X64_0x7E_VRF_ucx01000!Urb_USBPORTStyle_ProcessURB+98

Followup: MachineOwner

Any idea of what could be happening?

Kind regards,

Rui

On May 27, 2018, at 2:39 PM, xxxxx@protonmail.com wrote:
>
> Thanks a lot for the feedback.
> ,
> FAULTING_IP:
> Wdf01000!imp_WdfObjectGetTypedContextWorker+26
> fffff8000040f066 4c8b5010 mov r10,qword ptr [rax+10h]<br>&gt; ,,,<br>&gt; rax=0000307ffee7b428 rbx=0000000000000000 rcx=0000000000000000<br>&gt; rdx=ffffcf8001184bd0 rsi=0000000000000000 rdi=ffffd0002078ca40<br>&gt; rip=fffff8000040f066 rsp=ffffd0002078c6c0 rbp=000000000000000b<br>&gt; r8=fffff80001bb00b0 r9=fffff801ea1156b0 r10=ffffe0000518fc70<br>&gt; r11=0000000000000000 r12=0000000000000021 r13=0000000000000000<br>&gt; r14=00001ffffae70858 r15=ffffcf800111ed30<br>&gt; iopl=0 nv up ei pl zr na po nc<br>&gt; cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010246<br>&gt; Wdf01000!imp_WdfObjectGetTypedContextWorker+0x26:<br>&gt; fffff8000040f066 4c8b5010 mov r10,qword ptr [rax+10h] ds:002b:0000307ffee7b438=????????????????<br><br>In this case, it's using a KMDF handle as if it were an address. Is it possible you accidentally inserted a KMDF handle into one of the IRP fields? That seems really unlikely.<br><br>&gt; ffffd0002078c770 fffff80000414d43 : ffffcf800111ed30 ffffe00000341818 000000000000000f ffffe000`0518f7a0 : ucx01000!RootHub_Pdo_EvtInternalDeviceControlIrpPreprocessCallback+0x448 <<<< Maybe this calls to the preprocess callbacks I have been reading about (WDF). I guess makes sense because URB is sent via IRP_MJ_INTERNAL_DEVICE_CONTROL

It does that in case the call came from user mode. It needs to be able to grab pointers from user-mode memory, and that requires preprocessing.

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.