Skip to content

[Bug] heap-based buffer overflow in CherryUSB ADB device class due to unchecked host-controlled data_length #11263

@XueDugu

Description

@XueDugu

RT-Thread Version

master (verified on commit 6a635e32d9f39ea015824927cee492620a05212f)

Hardware Type/Architectures

Any BSP enabling CherryUSB device ADB support

Develop Toolchain

GCC

Describe the bug

Affected Component

Field Detail
File components/drivers/usb/cherryusb/class/adb/usbd_adb.c
Path USB Device ADB receive path

Vulnerability Description

A heap-based buffer overflow exists in the CherryUSB ADB device class receive path.

rx_packet.payload is a fixed-size buffer defined as:

#define MAX_PAYLOAD_V1  (4 * 1024)
#define MAX_PAYLOAD_V2  (256 * 1024)
#define MAX_PAYLOAD     MAX_PAYLOAD_V1

struct adb_packet {
    USB_MEM_ALIGNX struct adb_msg msg;
    USB_MEM_ALIGNX uint8_t payload[USB_ALIGN_UP(MAX_PAYLOAD, CONFIG_USB_ALIGN_SIZE)];
};

However, the vulnerable receive path schedules the next bulk OUT read without validating that rx_packet.msg.data_length <= MAX_PAYLOAD:

if (adb_client.common_state == ADB_STATE_READ_MSG) {
    if (nbytes != sizeof(struct adb_msg)) {
        ...
        return;
    }

    if (rx_packet.msg.data_length) {
        adb_client.common_state = ADB_STATE_READ_DATA;
        usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr,
                           rx_packet.payload, rx_packet.msg.data_length);  // ← no bounds check
    }
}

Additionally, the code performs an unconditional null-terminator write:

rx_packet.payload[rx_packet.msg.data_length] = '\0';

This introduces two distinct issues:

  • If data_length > MAX_PAYLOAD — the host can trigger an out-of-bounds write into the fixed payload buffer.
  • If data_length == MAX_PAYLOAD — the null-terminator write is already one byte out of bounds (off-by-one).

Trigger Condition

This path is automatically reachable once the RT-Thread USB device is configured:

case USBD_EVENT_CONFIGURED:
    adb_client.common_state = ADB_STATE_READ_MSG;
    usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr,
                       (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
    break;

After enumeration and configuration, a malicious USB host can send a crafted ADB message header and trigger the bug without any further local action on the RT-Thread side.

Note: Before scheduling the oversized payload read, the code does not validate magic or data_crc32. The only check in the first stage is that the received header length equals sizeof(struct adb_msg).


Proof of Concept

A PoC can be implemented with a custom USB host, a libusb-based userspace program, or a USB fuzzing platform.

Expected PoC flow:

  1. Connect to an RT-Thread target exposing the CherryUSB ADB device class.
  2. Complete normal USB enumeration until the device enters USBD_EVENT_CONFIGURED.
  3. Send one bulk OUT transfer containing a 24-byte adb_msg header with:
    • Any valid ADB command value (e.g. A_OPEN or A_WRTE)
    • data_length = MAX_PAYLOAD + 0x100 (or MAX_PAYLOAD for the off-by-one case)
  4. Send a second bulk OUT transfer containing data_length bytes of payload.

Expected result:

  • The device schedules a read into rx_packet.payload using the oversized host-controlled length.
  • Memory beyond the payload buffer is overwritten.
  • Depending on allocator layout and platform, the target may crash, corrupt adjacent state, or behave unpredictably.

Impact Analysis

Upstream

  • The vulnerable logic is present in current master.
  • The path is automatically reachable in stock upstream once ADB device support is enabled and the device is connected to a USB host.
  • This is not a network-remote issue; it requires a malicious directly-connected USB host.

Downstream

  • Any downstream RT-Thread product enabling CherryUSB ADB device support is affected if it can be connected to an untrusted USB host.
  • Realistic attacker-controlled hosts include hostile PCs, debugging stations, docking stations, or malicious charging/data ports.
  • Products that expose ADB over USB for maintenance, shell, file sync, or factory/debug workflows are especially exposed.

Suggested Fix

Validate rx_packet.msg.data_length before calling usbd_ep_start_read():

if (rx_packet.msg.data_length > MAX_PAYLOAD) {
    adb_client.common_state = ADB_STATE_READ_MSG;
    usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr,
                       (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
    return;
}

Also ensure the null-terminator write is only performed when data_length < MAX_PAYLOAD:

if (rx_packet.msg.data_length < MAX_PAYLOAD) {
    rx_packet.payload[rx_packet.msg.data_length] = '\0';
}

Please let us know if you intend to request a CVE ID upon confirmation of this vulnerability.

Other additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions