-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
RT-Thread Version
master (verified on commit 6a635e3); also present in v5.2.2, v5.2.1, and v5.2.0. Not present in v5.1.0 or earlier.
Hardware Type/Architectures
Any BSP enabling CherryUSB host audio/UAC support
Develop Toolchain
GCC
Describe the bug
Affected Components
| Field | Detail |
|---|---|
| File 1 | components/drivers/usb/cherryusb/class/audio/usbh_audio.c |
| File 2 | components/drivers/usb/cherryusb/class/audio/usbh_audio.h |
| File 3 | components/drivers/usb/cherryusb/class/audio/usb_audio.h |
| Function | static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf) |
Vulnerability Details
1. Fixed-capacity ac_msg_table can be overrun
The function allocates a fixed-size local array:
uint8_t input_offset = 0;
uint8_t output_offset = 0;
uint8_t feature_unit_offset = 0;
struct usbh_audio_ac_msg ac_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS];CONFIG_USBHOST_AUDIO_MAX_STREAMS defaults to 3. However, the parser increments these offsets with no bounds check:
memcpy(&ac_msg_table[input_offset].ac_input, desc,
sizeof(struct audio_cs_if_ac_input_terminal_descriptor));
input_offset++;
memcpy(&ac_msg_table[output_offset].ac_output, desc,
sizeof(struct audio_cs_if_ac_output_terminal_descriptor));
output_offset++;
memcpy(&ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength);
feature_unit_offset++;A malicious USB audio device can provide more than 3 AC descriptors and trigger an out-of-bounds write into the local stack array.
2. Variable-length descriptors copied into fixed-size structures
The parser copies variable-length USB Audio descriptors using the device-controlled bLength field:
memcpy(&ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength);memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1]
.as_format[cur_alt_setting],
desc, desc->bLength);Both destination objects are fixed-size. For example, FEATURE_UNIT is variable-length:
struct audio_cs_if_ac_feature_unit_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bUnitID;
uint8_t bSourceID;
uint8_t bControlSize;
uint8_t bmaControls[1];
uint8_t iFeature;
} __PACKED;And FORMAT_TYPE is also variable-length:
struct audio_cs_if_as_format_type_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bFormatType;
uint8_t bNrChannels;
uint8_t bSubframeSize;
uint8_t bBitResolution;
uint8_t bSamFreqType;
uint8_t tSamFreq[3];
} __PACKED;A crafted bLength can therefore directly cause out-of-bounds writes during enumeration.
Additionally, the parser trusts the descriptor-derived bSamFreqType count in a later loop:
for (uint8_t k = 0; k < audio_class->as_msg_table[i].as_format[j].bSamFreqType; k++) {
memcpy(&freq, &audio_class->as_msg_table[i].as_format[j].tSamFreq[3 * k], 3);
}This can produce additional out-of-bounds reads after the initial corruption.
Trigger Condition
The bug is reached automatically during USB host enumeration:
p = hport->raw_config_desc;
while (p[DESC_bLength]) {
...
case AUDIO_INTERFACE_DESCRIPTOR_TYPE:
...
/* parse attacker-controlled audio descriptors */
...
p += p[DESC_bLength];
}Note: This happens before
usbh_audio_run(), so no extra local action is required. A malicious USB audio device can trigger the vulnerability immediately when connected.
Proof of Concept
A PoC can be implemented with a programmable USB gadget/emulator such as Facedancer, LUNA, GreatFET, Linux USB gadget, or custom USB Audio firmware.
PoC 1: Overflow ac_msg_table
Present a malicious USB Audio configuration descriptor with more than 3 AudioControl descriptors of the same type (e.g. 4 AUDIO_CONTROL_INPUT_TERMINAL descriptors).
Expected result:
- Enumeration enters
usbh_audio_ctrl_connect() input_offsetexceedsCONFIG_USBHOST_AUDIO_MAX_STREAMS- The 4th copy writes past
ac_msg_table[] - The target may crash or corrupt stack state
PoC 2: Oversized FORMAT_TYPE or FEATURE_UNIT descriptor
Provide either a FEATURE_UNIT descriptor with an oversized bLength, or a FORMAT_TYPE descriptor with a large bSamFreqType and matching large bLength.
Expected result:
- The parser performs
memcpy(..., desc, desc->bLength)into a fixed-size destination - Memory past the destination object is overwritten during enumeration
Impact
A malicious directly-connected USB audio device can trigger memory corruption automatically during enumeration. Depending on target layout and build configuration, this may cause crashes or undefined behavior.
Upstream / Downstream Impact
Upstream
The vulnerable parser logic is present in current upstream master and is reachable in the default host enumeration path when CherryUSB host audio support is enabled.
Downstream
Any downstream RT-Thread product that enables CherryUSB host audio/UAC support and accepts untrusted USB audio devices may be affected.
Suggested Fix
The parser should reject malformed descriptors before copying them.
A. Bound-check all AC descriptor table indexes
if (input_offset >= CONFIG_USBHOST_AUDIO_MAX_STREAMS) {
USB_LOG_ERR("Too many input terminal descriptors\r\n");
return -USB_ERR_INVAL;
}
if (output_offset >= CONFIG_USBHOST_AUDIO_MAX_STREAMS) {
USB_LOG_ERR("Too many output terminal descriptors\r\n");
return -USB_ERR_INVAL;
}
if (feature_unit_offset >= CONFIG_USBHOST_AUDIO_MAX_STREAMS) {
USB_LOG_ERR("Too many feature unit descriptors\r\n");
return -USB_ERR_INVAL;
}B. Reject oversized FEATURE_UNIT descriptors
if (desc->bLength > sizeof(ac_msg_table[feature_unit_offset].ac_feature_unit)) {
USB_LOG_ERR("Feature unit descriptor too large: %u\r\n", desc->bLength);
return -USB_ERR_INVAL;
}
memcpy(&ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength);C. Reject oversized FORMAT_TYPE descriptors
if (desc->bLength > sizeof(audio_class->as_msg_table[0].as_format[0])) {
USB_LOG_ERR("Format type descriptor too large: %u\r\n", desc->bLength);
return -USB_ERR_INVAL;
}
memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1]
.as_format[cur_alt_setting],
desc, desc->bLength);D. Validate cur_iface - audio_class->ctrl_intf - 1
uint8_t stream_idx = cur_iface - audio_class->ctrl_intf - 1;
if (stream_idx >= CONFIG_USBHOST_AUDIO_MAX_STREAMS) {
USB_LOG_ERR("Audio stream index overflow: %u\r\n", stream_idx);
return -USB_ERR_INVAL;
}E. Validate bSamFreqType before iteration
if (audio_class->as_msg_table[i].as_format[j].bSamFreqType > 1) {
USB_LOG_ERR("Unsupported or oversized bSamFreqType: %u\r\n",
audio_class->as_msg_table[i].as_format[j].bSamFreqType);
return -USB_ERR_INVAL;
}A more complete fix is to stop storing variable-length descriptors in fixed-size structures, and instead either copy only the fixed header fields, or allocate a properly sized buffer based on a validated
bLength.
Kindly let me know if you intend to request a CVE ID upon confirmation of the vulnerability.
Other additional context
No response