763 lines
27 KiB
C
763 lines
27 KiB
C
/*--
|
|
Copyright (c) 1998, 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
urb.c
|
|
|
|
Abstract:
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
|
|
#include "usbverfy.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, UsbVerify_CheckReplacedUrbs)
|
|
#endif
|
|
|
|
#define COMPUTE_SIZE ((USHORT) 0xFFFF)
|
|
|
|
USHORT FunctionToUrbLength[] =
|
|
{
|
|
/* URB_FUNCTION_SELECT_CONFIGURATION 0x0000 */ COMPUTE_SIZE, // sizeof(struct _URB_SELECT_CONFIGURATION),
|
|
/* URB_FUNCTION_SELECT_INTERFACE 0x0001 */ COMPUTE_SIZE, // sizeof(struct _URB_SELECT_INTERFACE),
|
|
/* URB_FUNCTION_ABORT_PIPE 0x0002 */ sizeof(struct _URB_PIPE_REQUEST),
|
|
/* URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL 0x0003 */ sizeof(struct _URB_FRAME_LENGTH_CONTROL),
|
|
/* URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL 0x0004 */ sizeof(struct _URB_FRAME_LENGTH_CONTROL),
|
|
/* URB_FUNCTION_GET_FRAME_LENGTH 0x0005 */ sizeof(struct _URB_GET_FRAME_LENGTH),
|
|
/* URB_FUNCTION_SET_FRAME_LENGTH 0x0006 */ sizeof(struct _URB_SET_FRAME_LENGTH),
|
|
/* URB_FUNCTION_GET_CURRENT_FRAME_NUMBER 0x0007 */ sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER),
|
|
/* URB_FUNCTION_CONTROL_TRANSFER 0x0008 */ sizeof(struct _URB_CONTROL_TRANSFER),
|
|
/* URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 */ sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
|
|
/* URB_FUNCTION_ISOCH_TRANSFER 0x000A */ COMPUTE_SIZE, // sizeof(struct _URB_ISOCH_TRANSFER),
|
|
/* URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
/* URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE 0x000C */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
/* URB_FUNCTION_SET_FEATURE_TO_DEVICE 0x000D */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_SET_FEATURE_TO_INTERFACE 0x000E */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_SET_FEATURE_TO_ENDPOINT 0x000F */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
|
|
/* URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE 0x0010 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE 0x0011 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT 0x0012 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_GET_STATUS_FROM_DEVICE 0x0013 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ),
|
|
/* URB_FUNCTION_GET_STATUS_FROM_INTERFACE 0x0014 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ),
|
|
/* URB_FUNCTION_GET_STATUS_FROM_ENDPOINT 0x0015 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ),
|
|
/* URB_FUNCTION_RESERVED0 0x0016 */ 0x0,
|
|
/* URB_FUNCTION_VENDOR_DEVICE 0x0017 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_VENDOR_INTERFACE 0x0018 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_VENDOR_ENDPOINT 0x0019 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_CLASS_DEVICE 0x001A */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_CLASS_INTERFACE 0x001B */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_CLASS_ENDPOINT 0x001C */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_RESERVED 0x001D */ 0x0,
|
|
/* URB_FUNCTION_RESET_PIPE 0x001E */ sizeof(struct _URB_PIPE_REQUEST ),
|
|
/* URB_FUNCTION_CLASS_OTHER 0x001F */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
|
|
/* URB_FUNCTION_VENDOR_OTHER 0x0020 */ sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST ),
|
|
/* URB_FUNCTION_GET_STATUS_FROM_OTHER 0x0021 */ sizeof(struct _URB_CONTROL_GET_STATUS_REQUEST ),
|
|
/* URB_FUNCTION_CLEAR_FEATURE_TO_OTHER 0x0022 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_SET_FEATURE_TO_OTHER 0x0023 */ sizeof(struct _URB_CONTROL_FEATURE_REQUEST ),
|
|
/* URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT 0x0024 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
/* URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT 0x0025 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
/* URB_FUNCTION_GET_CONFIGURATION 0x0026 */ sizeof(struct _URB_CONTROL_GET_CONFIGURATION_REQUEST ),
|
|
/* URB_FUNCTION_GET_INTERFACE 0x0027 */ sizeof(struct _URB_CONTROL_GET_INTERFACE_REQUEST ),
|
|
/* URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE 0x0028 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
/* URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE 0x0029 */ sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
|
|
|
};
|
|
|
|
#define FUNCTION_TO_URB_LENGTH_MAX (sizeof(FunctionToUrbLength) / sizeof(USHORT))
|
|
|
|
USHORT
|
|
UsbVerify_DetermineInterfaceSize(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
|
|
PURB Urb,
|
|
PUSBD_INTERFACE_INFORMATION Interface
|
|
)
|
|
{
|
|
USHORT minimumLength;
|
|
|
|
//
|
|
// This assert is an iffy one. It is possible that a device can
|
|
// set a zero BW interface which could be either an alternate setting
|
|
// that contains 1 EP with a MaxPacketSize of 0 or an 0 EP interface.
|
|
// Right now we'll leave this in and just choke on most streaming
|
|
// devices that rachet down on shutting off.
|
|
//
|
|
// NOTE: This assert needs to be revisited for further determination
|
|
// of its validity. At a minimum, the above behavior should
|
|
// be documented somewhere.
|
|
//
|
|
|
|
UsbVerify_ASSERT(Interface->NumberOfPipes > 0,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
Urb);
|
|
|
|
//
|
|
// Calculate the minimum required length of the interface field. This must
|
|
// equal the sizeof interface plus any room for the pipe information.
|
|
// We subtract the sizeof(USBD_PIPE_INFORMATION) from the interface
|
|
// size since an interface doesn't have to have any pipes but
|
|
// sizeof(USBD_INTERFACE_INFORMATION) returns the size of one pipe.
|
|
//
|
|
|
|
minimumLength = sizeof(USBD_INTERFACE_INFORMATION) -
|
|
sizeof(USBD_PIPE_INFORMATION) +
|
|
Interface -> NumberOfPipes * sizeof(USBD_PIPE_INFORMATION);
|
|
|
|
|
|
if (Interface->Length)
|
|
{
|
|
//
|
|
// Check that the reported interface length is at least the size
|
|
// of the minimum length.
|
|
//
|
|
// NOTE: Need to find a better way to report the mimimum length
|
|
// to the user. This is the same problem as with
|
|
// verifying the overall URB length.
|
|
//
|
|
// NOTE: Should this be an == instead of >= to perform tighter
|
|
// restrictions. Doing so would help verify that the
|
|
// length field is accurate and not just some bogus
|
|
// number that happened to be bigger than minimum length.
|
|
// Are there reasons a client may legally specify a larger
|
|
// size?
|
|
//
|
|
|
|
UsbVerify_ASSERT(Interface->Length >= minimumLength,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
Urb);
|
|
|
|
return Interface->Length;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Size of the Interface field
|
|
//
|
|
|
|
return (minimumLength);
|
|
}
|
|
}
|
|
|
|
USHORT
|
|
UsbVerify_DetermineUrbLength(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
|
|
PURB Urb
|
|
)
|
|
{
|
|
USHORT length, function;
|
|
|
|
function = Urb->UrbHeader.Function;
|
|
|
|
if (function < FUNCTION_TO_URB_LENGTH_MAX) {
|
|
length = FunctionToUrbLength[function];
|
|
|
|
if (length != 0 && length != COMPUTE_SIZE) {
|
|
return length;
|
|
}
|
|
}
|
|
else {
|
|
UsbVerify_Print(DeviceExtension, PRINT_ALWAYS,
|
|
("Uknnown function 0x%x with length of 0!\n",
|
|
(ULONG) function));
|
|
|
|
return (USHORT) 0x0;
|
|
}
|
|
|
|
|
|
//
|
|
// Check to see if the user is allowed to send this URB
|
|
//
|
|
if (length == 0) {
|
|
UsbVerify_Assert("Client driver is not allowed to send this urb!",
|
|
DeviceExtension->Self, NULL, Urb, NULL);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// length == COMPUTE_SIZE. This means we need to do some math to figure
|
|
// out the size that is required
|
|
//
|
|
switch (function) {
|
|
case URB_FUNCTION_SELECT_CONFIGURATION:
|
|
|
|
//
|
|
// The UrbSelectConfiguration size should be at least equal to
|
|
// sizeof(UrbSelectConfiguration). This would be true only when
|
|
// the configuration is being deselected (ConfigurationDescriptor ==
|
|
// NULL). Otherwise, calculate the variable length portion that is
|
|
// the interface information. Since, sizeof(UrbSelectConfiguration)
|
|
// contains the size of the Interface field, subtract that off because
|
|
// the value returned by UsbVerify_DeterminteInterfaceSize contains
|
|
// the size of the Interface field as well as the memory hanging
|
|
// on after it.
|
|
//
|
|
|
|
length = (USHORT) (sizeof(Urb->UrbSelectConfiguration));
|
|
|
|
if (NULL != Urb->UrbSelectConfiguration.ConfigurationDescriptor)
|
|
{
|
|
length -= sizeof(Urb->UrbSelectConfiguration.Interface);
|
|
|
|
length += UsbVerify_DetermineInterfaceSize(DeviceExtension,
|
|
Urb,
|
|
&Urb->UrbSelectConfiguration.Interface);
|
|
}
|
|
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_INFO,
|
|
("select config computed size = 0x%x bytes\n", (ULONG) length));
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_SELECT_INTERFACE:
|
|
|
|
//
|
|
// The sizeof UrbSelectInterface contains the size of the Interface
|
|
// field. We want to get subtract that off because the value returned by
|
|
// UsbVerify_DeterminteInterfaceSize contains the size of the Interface
|
|
// field as well as the memory hanging on after it
|
|
//
|
|
length = (USHORT)
|
|
(sizeof(Urb->UrbSelectInterface) -
|
|
sizeof(Urb->UrbSelectInterface.Interface)) +
|
|
UsbVerify_DetermineInterfaceSize(DeviceExtension,
|
|
Urb,
|
|
&Urb->UrbSelectInterface.Interface);
|
|
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_INFO,
|
|
("select interface computed size = 0x%x bytes\n", (ULONG) length));
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_ISOCH_TRANSFER:
|
|
UsbVerify_ASSERT(Urb->UrbIsochronousTransfer.NumberOfPackets > 0,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
Urb);
|
|
length = sizeof(Urb->UrbIsochronousTransfer) +
|
|
(sizeof(Urb->UrbIsochronousTransfer.IsoPacket[0]) *
|
|
(Urb->UrbIsochronousTransfer.NumberOfPackets - 1));
|
|
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_INFO,
|
|
("isoch transfer computed size = 0x%x bytes\n", (ULONG) length));
|
|
|
|
break;
|
|
|
|
default:
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_ERROR,
|
|
("need to add a case for funciton 0x%x\n", (ULONG) function));
|
|
DbgBreakPoint();
|
|
length = 0;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbVerify_PreProcessUrb(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
|
|
PIRP Irp,
|
|
PURB *OriginalUrb
|
|
)
|
|
{
|
|
PURB urb, newUrb = NULL;
|
|
USHORT expectedUrbLength;
|
|
|
|
urb = URB_FROM_IRP(Irp);
|
|
*OriginalUrb = NULL;
|
|
|
|
//
|
|
// All URBs must definitely be sent only after START_DEVICE has been sent
|
|
// down and up the stack. Most USB clients do not handle SURPRISE_REMOVE,
|
|
// they will continue to send requests in that state.
|
|
//
|
|
UsbVerify_ASSERT((DeviceExtension->VerifyState == Started) ||
|
|
(DeviceExtension->VerifyState == SurpriseRemoved),
|
|
DeviceExtension->Self,
|
|
Irp,
|
|
urb);
|
|
|
|
UsbVerify_ASSERT(DeviceExtension->PowerState == PowerDeviceD0,
|
|
DeviceExtension->Self,
|
|
Irp,
|
|
urb);
|
|
|
|
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TEST_MEMORY)) {
|
|
//
|
|
// NOTE implement this
|
|
//
|
|
// MmProbeForWrite () ?
|
|
// ExQueryPoolBlockSize () ?
|
|
// any Mm function to verify the pool?
|
|
}
|
|
|
|
//
|
|
// Check the length that the calling driver passed in to the urb
|
|
// function. We should be able to determine the expected length in
|
|
// all cases and the length field should match that length.
|
|
//
|
|
|
|
expectedUrbLength = UsbVerify_DetermineUrbLength(DeviceExtension, urb);
|
|
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_INFO,
|
|
("expected urb length = 0x%x bytes\n", expectedUrbLength));
|
|
|
|
UsbVerify_ASSERT(urb->UrbHeader.Length == expectedUrbLength,
|
|
DeviceExtension->Self,
|
|
Irp,
|
|
urb);
|
|
|
|
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_REPLACE_URBS)) {
|
|
|
|
if (0 == expectedUrbLength)
|
|
{
|
|
goto LowMemory;
|
|
}
|
|
|
|
//
|
|
// Can't use AllocStruct because the urb size can be easily be bigger
|
|
// then sizeof(URB)
|
|
//
|
|
|
|
newUrb = (PURB) ExAllocatePool (NonPagedPool, expectedUrbLength);
|
|
|
|
if (newUrb != NULL) {
|
|
//
|
|
// Copy the contents into the new urb and then fill the original with
|
|
// USB_VERIFY_FULL. We will copy back the contents when we are done
|
|
// in the completion routine.
|
|
//
|
|
|
|
RtlCopyMemory(newUrb, urb, expectedUrbLength);
|
|
|
|
newUrb -> UrbHeader.Length = expectedUrbLength;
|
|
|
|
RtlFillMemory(urb, expectedUrbLength, USB_VERIFY_FILL);
|
|
|
|
//
|
|
// Replace the caller supplied URB with ours
|
|
//
|
|
|
|
URB_FROM_IRP(Irp) = newUrb;
|
|
|
|
*OriginalUrb = urb;
|
|
|
|
UsbVerify_Print(DeviceExtension, PRINT_URB_INFO,
|
|
("Replacing urb\n"
|
|
"\t Original URB 0x%x\n"
|
|
"\t Verify URB 0x%x\n"
|
|
"\t in IRP 0x%x on device 0x%x\n",
|
|
urb, newUrb, Irp, DeviceExtension->Self));
|
|
}
|
|
else {
|
|
LowMemory:
|
|
//
|
|
// No memory! Behave as if the replace urb flag is not set
|
|
//
|
|
InterlockedIncrement(&DeviceExtension->ReplaceUrbsLowMemoryCount);
|
|
}
|
|
}
|
|
|
|
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TRACK_URBS)) {
|
|
PUSB_VERIFY_TRACK_URB verify;
|
|
PLIST_ENTRY entry;
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Make sure the client is reusing the same URB in two different IRPs.
|
|
// According to JD, some driver writers actually try to do this!
|
|
//
|
|
UsbVerify_LockUrbList(DeviceExtension, irql);
|
|
|
|
for (entry = DeviceExtension->UrbList.Flink;
|
|
entry != &DeviceExtension->UrbList;
|
|
/* increment handled before we free the struct */ ) {
|
|
|
|
verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link);
|
|
|
|
//
|
|
// Any previous client sent URBs will be in verify->Urb (we are NOT
|
|
// replacing URBs( or in verify->OriginalUrb (we ARE replacing URBs)
|
|
//
|
|
UsbVerify_ASSERT(verify->Urb != urb && verify->OriginalUrb != urb,
|
|
DeviceExtension->Self,
|
|
Irp,
|
|
urb);
|
|
|
|
entry = entry->Flink;
|
|
}
|
|
|
|
UsbVerify_UnlockUrbList(DeviceExtension, irql);
|
|
|
|
verify = AllocStruct(NonPagedPool, USB_VERIFY_TRACK_URB, 1);
|
|
|
|
if (verify) {
|
|
RtlZeroMemory(verify, sizeof(*verify));
|
|
|
|
verify->Irp = Irp;
|
|
if (newUrb) {
|
|
verify->Urb = newUrb;
|
|
verify->OriginalUrb = urb;
|
|
}
|
|
else {
|
|
verify->Urb = urb;
|
|
verify->OriginalUrb = NULL;
|
|
}
|
|
|
|
KeQueryTickCount(&verify->ArrivalTime);
|
|
|
|
UsbVerify_LockUrbList(DeviceExtension, irql);
|
|
InsertTailList(&DeviceExtension->UrbList, &verify->Link);
|
|
UsbVerify_UnlockUrbList(DeviceExtension, irql);
|
|
}
|
|
else {
|
|
InterlockedIncrement(&DeviceExtension->UrbListLowMemoryCount);
|
|
}
|
|
}
|
|
|
|
InterlockedIncrement(&DeviceExtension->UrbListCount);
|
|
}
|
|
|
|
VOID
|
|
UsbVerify_PostProcessUrb(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PURB OldUrb
|
|
)
|
|
{
|
|
PUSB_VERIFY_DEVICE_EXTENSION devExt;
|
|
PURB urb;
|
|
ULONG urbListCount;
|
|
|
|
devExt = GetExtension(DeviceObject);
|
|
|
|
//
|
|
// This urb may be an urb we sent in place of OldUrb, if OldUrb != NULL
|
|
//
|
|
urb = URB_FROM_IRP(Irp);
|
|
|
|
//
|
|
// We always keep (at least) a count of pending urbs
|
|
//
|
|
urbListCount = InterlockedDecrement(&devExt->UrbListCount);
|
|
UsbVerify_ASSERT(urbListCount >= 0, devExt->Self, Irp, urb);
|
|
|
|
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_TRACK_URBS)) {
|
|
|
|
PUSB_VERIFY_TRACK_URB verify;
|
|
PLIST_ENTRY entry;
|
|
KIRQL irql;
|
|
BOOLEAN found = FALSE;
|
|
|
|
UsbVerify_LockUrbList(devExt, irql);
|
|
|
|
for (entry = devExt->UrbList.Flink;
|
|
entry != &devExt->UrbList;
|
|
entry = entry->Flink) {
|
|
|
|
verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link);
|
|
if (verify->Irp == Irp) {
|
|
UsbVerify_ASSERT(urb == verify->Urb, devExt->Self, Irp, urb);
|
|
|
|
RemoveEntryList(&verify->Link);
|
|
found = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
UsbVerify_UnlockUrbList(devExt, irql);
|
|
|
|
if (found) {
|
|
ExFreePool(verify);
|
|
}
|
|
else {
|
|
ULONG urbListLowMemoryCount;
|
|
|
|
//
|
|
// We go through the troule of assigning the return value to a local
|
|
// variable so it will be easier to inspect this value in the debugger
|
|
// if we need to view this value
|
|
//
|
|
urbListLowMemoryCount =
|
|
InterlockedDecrement(&devExt->UrbListLowMemoryCount);
|
|
|
|
UsbVerify_ASSERT(urbListLowMemoryCount >= 0, devExt->Self, Irp, urb);
|
|
}
|
|
}
|
|
|
|
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_REPLACE_URBS)) {
|
|
if (ARGUMENT_PRESENT(OldUrb)) {
|
|
PUCHAR buffer, bufferEnd;
|
|
|
|
|
|
buffer = (PUCHAR) OldUrb;
|
|
bufferEnd = buffer + urb->UrbHeader.Length;
|
|
|
|
//
|
|
// Make sure the caller hasn't scribbled on the URB.
|
|
//
|
|
// NTOE: is there a way for us to mark the memory as READ ONLY?
|
|
//
|
|
for ( ; buffer < bufferEnd; buffer++) {
|
|
UsbVerify_ASSERT(*buffer == USB_VERIFY_FILL,
|
|
devExt->Self,
|
|
Irp,
|
|
OldUrb);
|
|
}
|
|
|
|
UsbVerify_Print(devExt, PRINT_URB_INFO,
|
|
("returning original URB\n"
|
|
"\t Original URB 0x%x\n"
|
|
"\t Verify URB 0x%x\n"
|
|
"\t in IRP 0x%x on device 0x%x\n",
|
|
OldUrb, urb, Irp, devExt->Self));
|
|
|
|
//
|
|
// Copy the contents from our replacement URB into the caller's URB
|
|
// NOTE: the length field in the urb that we use in this copy
|
|
// is the calculated expected urb length. There may
|
|
// be problems if the client was expecting more data in the
|
|
// original urb or had not allocated enough space to hold
|
|
// everything.
|
|
//
|
|
|
|
RtlCopyMemory(OldUrb, urb, urb->UrbHeader.Length);
|
|
|
|
//
|
|
// We are done with our replacement URB. Free it and set up urb to
|
|
// point to valid pool.
|
|
//
|
|
|
|
ExFreePool(urb);
|
|
urb = OldUrb;
|
|
|
|
//
|
|
// This is necessary because we are more often than not going to play
|
|
// with this urb after this function call. The current URB in the IRP
|
|
// has just been freed, so we need to put the old and valid one back
|
|
//
|
|
URB_FROM_IRP(Irp) = OldUrb;
|
|
}
|
|
else {
|
|
ULONG replaceUrbsLowMemoryCount;
|
|
|
|
//
|
|
// We go through the troule of assigning the return value to a local
|
|
// variable so it will be easier to inspect this value in the debugger
|
|
// if we need to view this value
|
|
//
|
|
replaceUrbsLowMemoryCount =
|
|
InterlockedDecrement(&devExt->ReplaceUrbsLowMemoryCount);
|
|
|
|
UsbVerify_ASSERT(replaceUrbsLowMemoryCount >= 0,
|
|
devExt->Self, Irp, urb);
|
|
}
|
|
}
|
|
|
|
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_TEST_MEMORY)) {
|
|
//
|
|
// NOTE: implement this
|
|
//
|
|
// MmProbeForWrite () ?
|
|
// ExQueryPoolBlockSize () ?
|
|
// any Mm function to verify the pool?
|
|
}
|
|
|
|
//
|
|
// NOTE: should we check anything else on the way back up? Pipes for instance?
|
|
//
|
|
}
|
|
|
|
VOID
|
|
UsbVerify_FreePendingUrbsList(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
{
|
|
PUSB_VERIFY_TRACK_URB verify;
|
|
PLIST_ENTRY entry;
|
|
ULONG urbListCount,
|
|
urbListLowMemoryCount;
|
|
KIRQL irql;
|
|
|
|
//
|
|
// We go through the trouble of assigning the return value to a local
|
|
// variable so it will be easier to inspect this value in the debugger
|
|
// if we need to view this value
|
|
//
|
|
|
|
//
|
|
// NOTE: Looks like alot of clients don't cancel any of their pending requests
|
|
// until after they have the remove device. Perhaps this should be
|
|
// guarded by a VerifyFlags (instead of the #if 0) ?
|
|
//
|
|
urbListCount = InterlockedCompareExchange(&DeviceExtension->UrbListCount,
|
|
0,
|
|
0);
|
|
|
|
UsbVerify_ASSERT(urbListCount == 0,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
NULL);
|
|
|
|
urbListLowMemoryCount =
|
|
InterlockedCompareExchange(&DeviceExtension->UrbListLowMemoryCount,
|
|
0,
|
|
0);
|
|
|
|
UsbVerify_ASSERT(urbListLowMemoryCount == 0,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TRACK_URBS)) {
|
|
|
|
UsbVerify_LockUrbList(DeviceExtension, irql);
|
|
|
|
for (entry = DeviceExtension->UrbList.Flink;
|
|
entry != &DeviceExtension->UrbList;
|
|
/* increment handled before we free the struct */ ) {
|
|
|
|
verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link);
|
|
|
|
UsbVerify_ASSERT(verify->Urb != NULL && verify->Irp != NULL,
|
|
// "pended URB after remove device",
|
|
DeviceExtension->Self,
|
|
verify->Irp,
|
|
verify->Urb);
|
|
|
|
UsbVerify_LogError(IDS_STALE_URB,
|
|
DeviceExtension,
|
|
verify->Irp,
|
|
verify->Urb);
|
|
|
|
entry = entry->Flink;
|
|
|
|
RemoveEntryList(&verify->Link);
|
|
ExFreePool(verify);
|
|
}
|
|
|
|
UsbVerify_UnlockUrbList(DeviceExtension, irql);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UsbVerify_CheckReplacedUrbs(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
{
|
|
ULONG replaceUrbsLowMemoryCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We go through the troule of assigning the return value to a local
|
|
// variable so it will be easier to inspect this value in the debugger
|
|
// if we need to view this value
|
|
//
|
|
replaceUrbsLowMemoryCount
|
|
= InterlockedCompareExchange(&DeviceExtension->ReplaceUrbsLowMemoryCount,
|
|
0,
|
|
0);
|
|
|
|
UsbVerify_ASSERT(replaceUrbsLowMemoryCount == 0,
|
|
DeviceExtension->Self,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
UsbVerify_UrbComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sanity check an URB on the way back up
|
|
|
|
--*/
|
|
{
|
|
PUSB_VERIFY_DEVICE_EXTENSION devExt = GetExtension(DeviceObject);
|
|
|
|
UsbVerify_PostVerifyUrb(DeviceObject, Irp);
|
|
|
|
UsbVerify_PostProcessUrb(DeviceObject, Irp, (PURB) Context);
|
|
|
|
//
|
|
// We are done with the irp, return the status code in the irp so the irp
|
|
// will progress up the stack
|
|
//
|
|
UsbVerify_Print(devExt, PRINT_URB_NOISE,
|
|
("UrbComplete, returning 0x%x\n", Irp->IoStatus.Status));
|
|
|
|
if (Irp->PendingReturned) {
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
//
|
|
// the IO completion code looks for STATUS_MORE_PROCESSING_REQUIRED, so any
|
|
// other value will do (don't waste valuable cycles dereffing
|
|
// Irp->IoStatus.Status)
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
UsbVerify_SendUrbSynchronously(
|
|
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
|
|
PIRP Irp,
|
|
PURB OldUrb
|
|
)
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
KeInitializeEvent(&event,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Don't use UsbVerify_UrbComplete because its context must be an PURB, not
|
|
// a PKEVENT
|
|
//
|
|
IoSetCompletionRoutine(Irp,
|
|
(PIO_COMPLETION_ROUTINE) UsbVerify_Complete,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE); // No need for Cancel
|
|
|
|
status = IoCallDriver(DeviceExtension->TopOfStack, Irp);
|
|
|
|
if (STATUS_PENDING == status) {
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
Executive, // Waiting for reason of a driver
|
|
KernelMode, // Waiting in kernel mode
|
|
FALSE, // No allert
|
|
NULL); // No timeout
|
|
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
UsbVerify_PostProcessUrb(DeviceExtension->Self, Irp, OldUrb);
|
|
|
|
return status;
|
|
}
|