Skip to content

dwc2/hcd: guard against zero ep_size to prevent divide-by-zero during enumeration#3526

Closed
Copilot wants to merge 3 commits into
masterfrom
copilot/fix-divide-by-zero-issue
Closed

dwc2/hcd: guard against zero ep_size to prevent divide-by-zero during enumeration#3526
Copilot wants to merge 3 commits into
masterfrom
copilot/fix-divide-by-zero-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 5, 2026

A device reporting bMaxPacketSize0 = 0 (malformed descriptor) causes an integer divide-by-zero in cal_packet_count() via tu_div_ceil(len, 0) when the DWC2 HCD attempts to compute packet count for a control transfer.

Changes

  • hcd_edpt_open() — extract ep_size from tu_edpt_packet_size() and TU_ASSERT(ep_size != 0) before writing to the hardware channel register; this fails the endpoint open gracefully (returns false) rather than allowing a zero value to propagate into transfer operations

  • cal_packet_count() — extend existing len == 0 early-return to also cover ep_size == 0 as defense-in-depth

// Before
hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep);

// After
const uint16_t ep_size = tu_edpt_packet_size(desc_ep);
TU_ASSERT(ep_size != 0); // zero mps = malformed descriptor; prevent hw register corruption
hcchar_bm->ep_size = ep_size;
// Before
if (len == 0) { return 1; }

// After
if (len == 0 || ep_size == 0) { return 1; }
Original prompt

This section details on the original issue you should resolve

<issue_title>DWC2 host divide-by-zero when connecting HID device (esp32-arduino)</issue_title>
<issue_description>### Operating System

MacOS

Commit SHA

TinyUSB 0.20.1 (as in esp32-arduino-lib-builder); note to confirm on upstream.

Board

ESP32S3-USB-OTG (host mode)

Firmware

  • Stack: TinyUSB host only (device_info-style enumeration).
  • Build: esp32-arduino-lib-builder with sketch calling tuh_descriptor_get_device(), tuh_task_ext(), etc., and DWC2 as the host controller.

Reproducible with any firmware that uses the DWC2 host driver and performs control transfers (e.g. get device descriptor) when a device is connected.

What happened ?

Integer divide-by-zero in the DWC2 host controller driver during USB host enumeration when a HID device (e.g. USB mouse) is connected.

  • Location: cal_packet_count() in src/portable/synopsys/dwc2/hcd_dwc2.c (around line 299), which calls tu_div_ceil(len, ep_size).
  • Cause: ep_size can be 0 when the default control endpoint (EP0) is used with an invalid or unset max packet size (e.g. bMaxPacketSize0 from the first 8 bytes of the device descriptor is 0 or not yet valid). Dividing by ep_size then triggers the crash.
  • Call path:
    tuh_descriptor_get_devicehcd_setup_sendhcd_edpt_xferedpt_xfer_kickoffchannel_xfer_startcal_packet_count(len, ep_size)tu_div_ceil(len, 0)IntegerDivideByZero.
    So the bug is: no guard against ep_size == 0 in the DWC2 HCD when computing packet count.

How to reproduce ?

  1. Build a TinyUSB host example (or custom host firmware) for a target that uses the DWC2 host driver (e.g. ESP32-S3 with USB host).
  2. Flash and run the firmware so it enumerates devices (e.g. device_info-style or any sketch that calls tuh_descriptor_get_device() and related APIs).
  3. Connect a USB HID device (e.g. USB mouse) to the host port.
  4. Observe: during enumeration (e.g. when sending the first control transfer or right after SET_ADDRESS), the MCU panics with IntegerDivideByZero in tu_div_ceil (called from cal_packet_count in hcd_dwc2.c).

Exact steps on our setup:

  • Use esp32-arduino-lib-builder with a host-only sketch (device_info-style) and target esp32s3.
  • Run idf.py -p <port> flash monitor.
  • Plug in a USB mouse.
  • Crash occurs during enumeration (get device descriptor / set address phase).

Sketch:

#include "Arduino.h"

/*
 * TinyUSB Host Device Info Example - Arduino Port
 * Based on tinyusb/examples/host/device_info
 * 
 * This example enumerates attached USB devices and prints their descriptors
*/

#if CONFIG_IDF_TARGET_ESP32S3
// Hardware pin definitions (OTG board specific)
#define BSP_USB_POS           (GPIO_NUM_20)
#define BSP_USB_NEG           (GPIO_NUM_19)
#define BSP_USB_MODE_SEL      (GPIO_NUM_18) // Select Host (high level) or Device (low level, default) mode
#define BSP_USB_HOST_VOLTAGE  (GPIO_NUM_1)  // Voltage at this pin = (V_BUS / 3.7), ADC1 channel 0
#define BSP_USB_HOST_VOLTAGE_DIV (3.7f)
#define BSP_USB_LIMIT_EN      (GPIO_NUM_17) // Active high (pulled low)
#define BSP_USB_DEV_VBUS_EN   (GPIO_NUM_12) // Active high (pulled low)

#define BSP_BATTERY_VOLTAGE   (GPIO_NUM_2)  // Voltage at this pin = (V_BAT / 2), ADC1 channel 1
#define BSP_BATTERY_VOLTAGE_DIV (2)
#define BSP_BATTERY_BOOST_EN  (GPIO_NUM_13) // 3.3->5V for USB device power from battery. Active high (pulled low)

#elif CONFIG_IDF_TARGET_ESP32P4
#define BSP_USB_POS           (GPIO_NUM_20)
#define BSP_USB_NEG           (GPIO_NUM_19)
#endif
// Include ESP-IDF headers for USB hardware initialization
#ifdef ESP_PLATFORM
  #include "esp_err.h"  // For esp_err_to_name()
  #if __has_include("esp_private/usb_phy.h")
    #include "esp_private/usb_phy.h"
    #define USB_PHY_AVAILABLE
  #elif __has_include("hal/usb_hal.h")
    #include "hal/usb_hal.h"
    #define USB_HAL_AVAILABLE
  #endif
#endif

// Include TinyUSB header
// The arduino_tinyusb component's tusb_config.h should already have
// CFG_TUH_ENABLED=1 if built from PR hathach/tinyusb#314
#include "tusb.h"

// USB PHY handle for host mode
#ifdef USB_PHY_AVAILABLE
static usb_phy_handle_t usb_phy_handle = NULL;
#endif

// English language ID
#define LANGUAGE_ID 0x0409

// Declare buffers for USB transfers (must be in USB/DMA section)
CFG_TUH_MEM_SECTION struct {
  TUH_EPBUF_TYPE_DEF(tusb_desc_device_t, device);
  TUH_EPBUF_DEF(serial, 64*sizeof(uint16_t));
  TUH_EPBUF_DEF(buf, 128*sizeof(uint16_t));
} desc;

//--------------------------------------------------------------------+
// String Descriptor Helper
//--------------------------------------------------------------------+

static void _convert_utf16le_to_utf8(const uint16_t* utf16, size_t utf16_len, uint8_t* utf8, size_t utf8_len) {
  (void) utf8_len;
  for (size_t i = 0; i < utf16_len; i++) {
    uint16_t chr = utf16[i];
    if (chr < 0x80) {
      *utf8++ = chr & 0xf...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes hathach/tinyusb#3525

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. [Learn more about Advanced Security.](https://gh.io/cca-advanced-security)

Copilot AI and others added 2 commits March 5, 2026 14:09
Co-authored-by: hathach <249515+hathach@users.noreply.github.com>
Co-authored-by: hathach <249515+hathach@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix divide-by-zero error in DWC2 host controller dwc2/hcd: guard against zero ep_size to prevent divide-by-zero during enumeration Mar 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 5, 2026

TinyUSB Average Code Size Metrics

File .text .rodata .data .bss size %
cdc_host.c 6610 487 15 1539 8371 5.2%
ehci.c 2761 0 0 5970 7537 4.7%
ncm_device.c 1514 28 1408 5830 7358 4.6%
usbh.c 4556 61 101 967 5647 3.5%
hcd_dwc2.c 4978 32 1 512 5523 3.4%
midi_host.c 1353 7 7 3740 5097 3.2%
video_device.c 4391 5 1851 472 4855 3.0%
audio_device.c 2849 0 1248 1673 4518 2.8%
dcd_dwc2.c 4174 25 0 265 4463 2.8%
ohci.c 1942 0 0 2414 4356 2.7%
dcd_ch32_usbfs.c 1472 0 0 2444 3916 2.4%
hcd_stm32_fsdev.c 3282 0 1 420 3703 2.3%
usbd.c 3191 57 89 276 3531 2.2%
ecm_rndis_device.c 1037 0 1 2272 3310 2.1%
dcd_khci.c 1953 0 0 1290 3243 2.0%
hcd_musb.c 3073 0 0 157 3230 2.0%
dcd_ci_fs.c 1925 0 0 1290 3215 2.0%
dcd_nrf5x.c 2919 0 0 292 3211 2.0%
dcd_da146xx.c 3067 0 0 144 3211 2.0%
hcd_rusb2.c 2923 0 0 245 3168 2.0%
dcd_rusb2.c 2917 0 0 156 3073 1.9%
msc_device.c 2518 108 2286 538 3056 1.9%
hcd_ch32_usbfs.c 2484 0 0 498 2982 1.9%
hcd_khci.c 2442 0 0 449 2891 1.8%
dcd_stm32_fsdev.c 2557 0 0 291 2848 1.8%
dcd_mm32f327x_otg.c 1478 0 0 1290 2768 1.7%
dcd_musb.c 2446 0 0 160 2606 1.6%
hcd_samd.c 2220 0 0 324 2544 1.6%
dcd_ci_hs.c 1762 0 0 1280 2530 1.6%
dcd_eptri.c 2270 0 0 259 2529 1.6%
usbtmc_device.c 2176 24 69 291 2500 1.6%
hid_host.c 1206 0 0 1250 2456 1.5%
mtp_device.c 1689 22 1449 579 2275 1.4%
dcd_rp2040.c 859 20 604 655 2138 1.3%
dcd_ch32_usbhs.c 1648 0 0 448 2096 1.3%
cdc_device.c 1328 16 19 661 1988 1.2%
msc_host.c 1589 0 0 394 1984 1.2%
dcd_msp430x5xx.c 1796 0 0 176 1972 1.2%
hcd_rp2040.c 976 73 416 384 1849 1.1%
dcd_lpc17_40.c 1470 0 0 648 1794 1.1%
midi_device.c 1127 0 991 589 1714 1.1%
dcd_nuc505.c 0 0 1529 157 1686 1.0%
dcd_lpc_ip3511.c 1463 0 0 264 1639 1.0%
dcd_samg.c 1319 0 0 72 1391 0.9%
dcd_samd.c 1032 0 0 266 1298 0.8%
hub.c 1235 8 8 29 1268 0.8%
dcd_nuc121.c 1167 0 0 101 1268 0.8%
hid_device.c 1118 44 997 115 1233 0.8%
dcd_nuc120.c 1093 0 0 78 1171 0.7%
vendor_device.c 624 0 530 464 1087 0.7%
rp2040_usb.c 172 75 718 4 969 0.6%
dfu_device.c 744 28 712 183 926 0.6%
typec_stm32.c 820 8 2 12 842 0.5%
tusb_fifo.c 843 0 477 0 838 0.5%
dwc2_common.c 601 30 0 0 618 0.4%
usbc.c 420 2 20 166 608 0.4%
usbd_control.c 523 0 474 78 600 0.4%
hcd_pio_usb.c 262 0 240 0 502 0.3%
tusb.c 428 0 368 3 430 0.3%
hcd_ci_hs.c 190 0 0 0 190 0.1%
fsdev_common.c 180 0 0 0 180 0.1%
rusb2_common.c 160 0 16 0 176 0.1%
dfu_rt_device.c 156 0 134 0 156 0.1%
TOTAL 113478 1160 16781 45524 161132 100.0%
Input files
  • cmake-build/cmake-build-adafruit_clue/metrics.json
  • cmake-build/cmake-build-apard32690/metrics.json
  • cmake-build/cmake-build-at32f403a_weact_blackpill/metrics.json
  • cmake-build/cmake-build-at_start_f402/metrics.json
  • cmake-build/cmake-build-at_start_f413/metrics.json
  • cmake-build/cmake-build-at_start_f415/metrics.json
  • cmake-build/cmake-build-at_start_f423/metrics.json
  • cmake-build/cmake-build-at_start_f425/metrics.json
  • cmake-build/cmake-build-at_start_f435/metrics.json
  • cmake-build/cmake-build-at_start_f455/metrics.json
  • cmake-build/cmake-build-b_g474e_dpow1/metrics.json
  • cmake-build/cmake-build-b_u585i_iot2a/metrics.json
  • cmake-build/cmake-build-ch32v103r_r1_1v0/metrics.json
  • cmake-build/cmake-build-ch32v203c_r0_1v0/metrics.json
  • cmake-build/cmake-build-ch32v307v_r1_1v0/metrics.json
  • cmake-build/cmake-build-cynthion_d11/metrics.json
  • cmake-build/cmake-build-da14695_dk_usb/metrics.json
  • cmake-build/cmake-build-double_m33_express/metrics.json
  • cmake-build/cmake-build-ea4088_quickstart/metrics.json
  • cmake-build/cmake-build-ea4357/metrics.json
  • cmake-build/cmake-build-ek_tm4c123gxl/metrics.json
  • cmake-build/cmake-build-feather_stm32f405/metrics.json
  • cmake-build/cmake-build-fomu/metrics.json
  • cmake-build/cmake-build-frdm_k32l2a4s/metrics.json
  • cmake-build/cmake-build-frdm_k64f/metrics.json
  • cmake-build/cmake-build-frdm_kl25z/metrics.json
  • cmake-build/cmake-build-frdm_mcxa153/metrics.json
  • cmake-build/cmake-build-frdm_rw612/metrics.json
  • cmake-build/cmake-build-hpm6750evk2/metrics.json
  • cmake-build/cmake-build-lpcxpresso11u37/metrics.json
  • cmake-build/cmake-build-lpcxpresso1347/metrics.json
  • cmake-build/cmake-build-lpcxpresso1549/metrics.json
  • cmake-build/cmake-build-lpcxpresso1769/metrics.json
  • cmake-build/cmake-build-lpcxpresso18s37/metrics.json
  • cmake-build/cmake-build-lpcxpresso51u68/metrics.json
  • cmake-build/cmake-build-lpcxpresso54114/metrics.json
  • cmake-build/cmake-build-metro_m0_express/metrics.json
  • cmake-build/cmake-build-metro_m4_express/metrics.json
  • cmake-build/cmake-build-metro_m7_1011/metrics.json
  • cmake-build/cmake-build-mm32f327x_mb39/metrics.json
  • cmake-build/cmake-build-msp_exp430f5529lp/metrics.json
  • cmake-build/cmake-build-msp_exp432e401y/metrics.json
  • cmake-build/cmake-build-nutiny_nuc126v/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc120/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc121/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc505/metrics.json
  • cmake-build/cmake-build-portenta_c33/metrics.json
  • cmake-build/cmake-build-raspberry_pi_pico/metrics.json
  • cmake-build/cmake-build-raspberrypi_cm4/metrics.json
  • cmake-build/cmake-build-raspberrypi_zero/metrics.json
  • cmake-build/cmake-build-samg55_xplained/metrics.json
  • cmake-build/cmake-build-sipeed_longan_nano/metrics.json
  • cmake-build/cmake-build-stlinkv3mini/metrics.json
  • cmake-build/cmake-build-stm32c071nucleo/metrics.json
  • cmake-build/cmake-build-stm32f070rbnucleo/metrics.json
  • cmake-build/cmake-build-stm32f103_bluepill/metrics.json
  • cmake-build/cmake-build-stm32f207nucleo/metrics.json
  • cmake-build/cmake-build-stm32f303disco/metrics.json
  • cmake-build/cmake-build-stm32g0b1nucleo/metrics.json
  • cmake-build/cmake-build-stm32h503nucleo/metrics.json
  • cmake-build/cmake-build-stm32h743eval/metrics.json
  • cmake-build/cmake-build-stm32h7s3nucleo/metrics.json
  • cmake-build/cmake-build-stm32l052dap52/metrics.json
  • cmake-build/cmake-build-stm32l412nucleo/metrics.json
  • cmake-build/cmake-build-stm32n6570dk/metrics.json
  • cmake-build/cmake-build-stm32u083cdk/metrics.json
  • cmake-build/cmake-build-stm32wb55nucleo/metrics.json
  • cmake-build/cmake-build-stm32wba_nucleo/metrics.json
  • cmake-build/cmake-build-xmc4500_relax/metrics.json

@hathach hathach closed this Mar 9, 2026
@HiFiPhile HiFiPhile deleted the copilot/fix-divide-by-zero-issue branch March 16, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants