reactos/drivers/usb/usbstor/descriptor.c
Adam Słaboń 20efea8fa4
[USBSTOR] Fix PdoHandleQueryInstanceId and increase serial number descriptor size to MAXIMUM_USB_STRING_LENGTH (#6413)
Serial number on some USB devices might exceed the number of 100 characters
(e.g. 120 characters on "SanDisk Ultra 3.2Gen1" pendrive) and cause buffer
overflow, resulting in usbstor.sys crash.

- Use pool allocation for instance ID generation.
  Fixes stack overflow on USB storage devices with large serial number.
- Print the LUN number as a hexadecimal, not as a character.
- Verify the serial number descriptor before using it.
- Increase the max descriptor size for serial number to
  MAXIMUM_USB_STRING_LENGTH. This fixes serial number string truncation.

Based on suggestions by disean and ThFabba.

CORE-17625
2024-02-16 18:48:33 +03:00

357 lines
12 KiB
C

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: USB block storage device driver.
* COPYRIGHT: 2005-2006 James Tabor
* 2011-2012 Michael Martin (michael.martin@reactos.org)
* 2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "usbstor.h"
#define NDEBUG
#include <debug.h>
NTSTATUS
NTAPI
USBSTOR_GetDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR DescriptorType,
IN ULONG DescriptorLength,
IN UCHAR DescriptorIndex,
IN LANGID LanguageId,
OUT PVOID *OutDescriptor)
{
PURB Urb;
NTSTATUS Status;
PVOID Descriptor;
ASSERT(DeviceObject);
ASSERT(OutDescriptor);
ASSERT(DescriptorLength);
// first allocate descriptor buffer
Descriptor = AllocateItem(NonPagedPool, DescriptorLength);
if (!Descriptor)
{
// no memory
return STATUS_INSUFFICIENT_RESOURCES;
}
Urb = (PURB) AllocateItem(NonPagedPool, sizeof(URB));
if (!Urb)
{
// no memory
FreeItem(Descriptor);
return STATUS_INSUFFICIENT_RESOURCES;
}
// initialize urb
UsbBuildGetDescriptorRequest(Urb,
sizeof(Urb->UrbControlDescriptorRequest),
DescriptorType,
DescriptorIndex,
LanguageId,
Descriptor,
NULL,
DescriptorLength,
NULL);
// submit urb
Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb);
FreeItem(Urb);
if (NT_SUCCESS(Status))
{
*OutDescriptor = Descriptor;
}
return Status;
}
NTSTATUS
USBSTOR_GetDescriptors(
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
PFDO_DEVICE_EXTENSION DeviceExtension;
USHORT DescriptorLength;
DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
// first get device descriptor
Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_DEVICE_DESCRIPTOR_TYPE, sizeof(USB_DEVICE_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->DeviceDescriptor);
if (!NT_SUCCESS(Status))
{
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
// now get basic configuration descriptor
Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, sizeof(USB_CONFIGURATION_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
if (!NT_SUCCESS(Status))
{
FreeItem(DeviceExtension->DeviceDescriptor);
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
// backup length
DescriptorLength = DeviceExtension->ConfigurationDescriptor->wTotalLength;
// release basic descriptor
FreeItem(DeviceExtension->ConfigurationDescriptor);
DeviceExtension->ConfigurationDescriptor = NULL;
// allocate full descriptor
Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, DescriptorLength, 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
if (!NT_SUCCESS(Status))
{
FreeItem(DeviceExtension->DeviceDescriptor);
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
// check if there is a serial number provided
if (DeviceExtension->DeviceDescriptor->iSerialNumber)
{
// get serial number
Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_STRING_DESCRIPTOR_TYPE, MAXIMUM_USB_STRING_LENGTH, DeviceExtension->DeviceDescriptor->iSerialNumber, 0x0409, (PVOID*)&DeviceExtension->SerialNumber);
if (!NT_SUCCESS(Status))
{
FreeItem(DeviceExtension->DeviceDescriptor);
DeviceExtension->DeviceDescriptor = NULL;
FreeItem(DeviceExtension->ConfigurationDescriptor);
DeviceExtension->ConfigurationDescriptor = NULL;
DeviceExtension->SerialNumber = NULL;
return Status;
}
}
return Status;
}
NTSTATUS
NTAPI
USBSTOR_ScanConfigurationDescriptor(
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
OUT PUSB_INTERFACE_DESCRIPTOR *OutInterfaceDescriptor,
OUT PUSB_ENDPOINT_DESCRIPTOR *InEndpointDescriptor,
OUT PUSB_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor)
{
PUSB_CONFIGURATION_DESCRIPTOR CurrentDescriptor;
PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
ASSERT(ConfigurationDescriptor);
ASSERT(OutInterfaceDescriptor);
ASSERT(InEndpointDescriptor);
ASSERT(OutEndpointDescriptor);
*OutInterfaceDescriptor = NULL;
*InEndpointDescriptor = NULL;
*OutEndpointDescriptor = NULL;
// start scanning
CurrentDescriptor = ConfigurationDescriptor;
do
{
if (CurrentDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE)
{
if (*OutInterfaceDescriptor)
{
// we only process the first interface descriptor as ms does -> see documentation
break;
}
// store interface descriptor
*OutInterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)CurrentDescriptor;
}
else if (CurrentDescriptor->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE)
{
EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)CurrentDescriptor;
ASSERT(*OutInterfaceDescriptor);
// get endpoint type
if ((EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK)
{
if (USB_ENDPOINT_DIRECTION_IN(EndpointDescriptor->bEndpointAddress))
{
*InEndpointDescriptor = EndpointDescriptor;
}
else
{
*OutEndpointDescriptor = EndpointDescriptor;
}
}
else if ((EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT)
{
UNIMPLEMENTED;
}
}
// move to next descriptor
CurrentDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)((ULONG_PTR)CurrentDescriptor + CurrentDescriptor->bLength);
// was it the last descriptor
if ((ULONG_PTR)CurrentDescriptor >= ((ULONG_PTR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength))
{
break;
}
} while(TRUE);
// check if everything has been found
if (*OutInterfaceDescriptor == NULL || *InEndpointDescriptor == NULL || *OutEndpointDescriptor == NULL)
{
DPRINT1("USBSTOR_ScanConfigurationDescriptor: Failed to find InterfaceDescriptor %p InEndpointDescriptor %p OutEndpointDescriptor %p\n", *OutInterfaceDescriptor, *InEndpointDescriptor, *OutEndpointDescriptor);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
VOID
DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
DPRINT1("Dumping ConfigurationDescriptor %p\n", ConfigurationDescriptor);
DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength);
DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType);
DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength);
DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces);
DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue);
DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration);
DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes);
DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower);
}
NTSTATUS
USBSTOR_SelectConfigurationAndInterface(
IN PDEVICE_OBJECT DeviceObject,
IN PFDO_DEVICE_EXTENSION DeviceExtension)
{
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
PUSB_ENDPOINT_DESCRIPTOR InEndpointDescriptor, OutEndpointDescriptor;
NTSTATUS Status;
PURB Urb;
PUSBD_INTERFACE_LIST_ENTRY InterfaceList;
Status = USBSTOR_ScanConfigurationDescriptor(DeviceExtension->ConfigurationDescriptor, &InterfaceDescriptor, &InEndpointDescriptor, &OutEndpointDescriptor);
if (!NT_SUCCESS(Status))
{
return Status;
}
// now allocate one interface entry and terminating null entry
InterfaceList = (PUSBD_INTERFACE_LIST_ENTRY)AllocateItem(PagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * 2);
if (!InterfaceList)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
// initialize interface list entry
InterfaceList[0].InterfaceDescriptor = InterfaceDescriptor;
// now allocate the urb
Urb = USBD_CreateConfigurationRequestEx(DeviceExtension->ConfigurationDescriptor, InterfaceList);
if (!Urb)
{
FreeItem(InterfaceList);
return STATUS_INSUFFICIENT_RESOURCES;
}
ASSERT(InterfaceList[0].Interface);
// submit urb
Status = USBSTOR_SyncUrbRequest(DeviceExtension->LowerDeviceObject, Urb);
if (!NT_SUCCESS(Status))
{
// failed to set configuration
DPRINT1("USBSTOR_SelectConfiguration failed to set interface %x\n", Status);
FreeItem(InterfaceList);
ExFreePoolWithTag(Urb, 0);
return Status;
}
// backup interface information
DeviceExtension->InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)AllocateItem(NonPagedPool, Urb->UrbSelectConfiguration.Interface.Length);
if (!DeviceExtension->InterfaceInformation)
{
FreeItem(InterfaceList);
ExFreePoolWithTag(Urb, 0);
return STATUS_INSUFFICIENT_RESOURCES;
}
// copy interface information
RtlCopyMemory(DeviceExtension->InterfaceInformation, &Urb->UrbSelectConfiguration.Interface, Urb->UrbSelectConfiguration.Interface.Length);
// store pipe handle
DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;
// now prepare interface urb
UsbBuildSelectInterfaceRequest(Urb, GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints), DeviceExtension->ConfigurationHandle, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting);
// copy interface information structure back - as offset for SelectConfiguration / SelectInterface request do differ
RtlCopyMemory(&Urb->UrbSelectInterface.Interface, DeviceExtension->InterfaceInformation, DeviceExtension->InterfaceInformation->Length);
// now select the interface
Status = USBSTOR_SyncUrbRequest(DeviceExtension->LowerDeviceObject, Urb);
if (NT_SUCCESS(Status))
{
// update configuration info
ASSERT(Urb->UrbSelectInterface.Interface.Length == DeviceExtension->InterfaceInformation->Length);
RtlCopyMemory(DeviceExtension->InterfaceInformation, &Urb->UrbSelectInterface.Interface, Urb->UrbSelectInterface.Interface.Length);
}
FreeItem(InterfaceList);
ExFreePoolWithTag(Urb, 0);
return Status;
}
NTSTATUS
USBSTOR_GetPipeHandles(
IN PFDO_DEVICE_EXTENSION DeviceExtension)
{
ULONG Index;
BOOLEAN BulkInFound = FALSE, BulkOutFound = FALSE;
// enumerate all pipes and extract bulk-in / bulk-out pipe handle
for (Index = 0; Index < DeviceExtension->InterfaceInformation->NumberOfPipes; Index++)
{
if (DeviceExtension->InterfaceInformation->Pipes[Index].PipeType == UsbdPipeTypeBulk)
{
if (USB_ENDPOINT_DIRECTION_IN(DeviceExtension->InterfaceInformation->Pipes[Index].EndpointAddress))
{
DeviceExtension->BulkInPipeIndex = Index;
// there should not be another bulk in pipe
ASSERT(BulkInFound == FALSE);
BulkInFound = TRUE;
}
else
{
DeviceExtension->BulkOutPipeIndex = Index;
// there should not be another bulk out pipe
ASSERT(BulkOutFound == FALSE);
BulkOutFound = TRUE;
}
}
}
if (!BulkInFound || !BulkOutFound)
{
// WTF? usb port driver does not give us bulk pipe access
DPRINT1("USBSTOR_GetPipeHandles> BulkInFound %c BulkOutFound %c missing!!!\n", BulkInFound, BulkOutFound);
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
return STATUS_SUCCESS;
}