The _ux_host_class_hid_report_descriptor_get() retrieves and parses a HID report descriptor from a USB HID device. It iteratively calls _ux_host_class_hid_report_item_analyse() to parse individual HID items, adjusting the descriptor pointer and remaining length as it goes.
A serious vulnerability exists in how this loop handles memory bounds:
- Insufficient bounds check:
The check:
if (length < item.ux_host_class_hid_item_report_length)
verifies only one part of the read (the report_length), but not the full read length:
descriptor += item.ux_host_class_hid_item_report_format;
descriptor += item.ux_host_class_hid_item_report_length;
- Integer underflow:
The line:
length -= (ULONG)(item.ux_host_class_hid_item_report_length + item.ux_host_class_hid_item_report_format);
assumes length is always greater than or equal to the total consumed size. If that’s not the case (e.g., length is 1, but the item reports a size of 16), length underflows and becomes a very large unsigned integer. The loop then continues indefinitely with a corrupted descriptor pointer, leading to out-of-bounds memory reads. This is possible because the length bounds check (see above [and below]) doesn't account for item.ux_host_class_hid_item_report_format
code:
UINT _ux_host_class_hid_report_descriptor_get(UX_HOST_CLASS_HID *hid, ULONG length)
{
...
descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, length);
...
transfer_request -> ux_transfer_request_data_pointer = descriptor;
transfer_request -> ux_transfer_request_requested_length = length;
transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR;
transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_INTERFACE;
...
status = _ux_host_stack_transfer_request(transfer_request);
...
/* Parse the report descriptor and build the report items. */
while (length)
{
...
_ux_host_class_hid_report_item_analyse(descriptor, &item);
...
descriptor += item.ux_host_class_hid_item_report_format;
...
descriptor += item.ux_host_class_hid_item_report_length;
/* Verify that the report descriptor is not corrupted. */
if (length < item.ux_host_class_hid_item_report_length) <-- this bounds check is insufficient
{
/* Return error status. */
status = (UX_DESCRIPTOR_CORRUPTED);
break;
}
/* Adjust the length. */
length -= (ULONG)(item.ux_host_class_hid_item_report_length + item.ux_host_class_hid_item_report_format); <-- length can underflow, at which point this loop goes off the rails
}
...
}
#define UX_HOST_CLASS_HID_ITEM_TAG_LONG 0xf0
UINT _ux_host_class_hid_report_item_analyse(UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item)
{
UCHAR item_byte;
/* Get the first byte from the descriptor. */
item_byte = *descriptor;
....
if ((item_byte & UX_HOST_CLASS_HID_ITEM_TAG_MASK) == UX_HOST_CLASS_HID_ITEM_TAG_LONG)
{
/* We have a long item, mark its format. */
item -> ux_host_class_hid_item_report_format = UX_HOST_CLASS_HID_ITEM_TAG_LONG;
/* Set the type. */
item -> ux_host_class_hid_item_report_type = (item_byte >> 2) & 3;
/* Get its length (byte 1). */
item -> ux_host_class_hid_item_report_length = (USHORT) *(descriptor + 1);
/* Then the tag (byte 2). */
item -> ux_host_class_hid_item_report_tag = *(descriptor + 2);
}
...
return(UX_SUCCESS);
}
The
_ux_host_class_hid_report_descriptor_get()retrieves and parses a HID report descriptor from a USB HID device. It iteratively calls_ux_host_class_hid_report_item_analyse()to parse individual HID items, adjusting the descriptor pointer and remaining length as it goes.A serious vulnerability exists in how this loop handles memory bounds:
The check:
verifies only one part of the read (the report_length), but not the full read length:
The line:
assumes length is always greater than or equal to the total consumed size. If that’s not the case (e.g., length is 1, but the item reports a size of 16), length underflows and becomes a very large unsigned integer. The loop then continues indefinitely with a corrupted descriptor pointer, leading to out-of-bounds memory reads. This is possible because the length bounds check (see above [and below]) doesn't account for item.ux_host_class_hid_item_report_format
code: