Skip to content

Possible infinite busy-loop in serial writer if the transport reports 0 bytes written repeatedly #319

Description

@popodai

Summary

write_serial() (main/serial.c:150-174) loops until all bytes are written, but only treats a return of -1 as an error:

int written = 0;
while (written != length) {
    const int wrote = tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, msg + written, length - written);
    // or on ESP32: uart_write_bytes(UART_NUM_0, msg + written, length - written);
    if (wrote == -1) {
        return false;
    }
    written += wrote;
}

If the transport returns 0 repeatedly — e.g. tinyusb_cdcacm_write_queue() when the USB-CDC TX buffer is full and no host is reading (cable unplugged, or host app killed mid-transfer), or uart_write_bytes() with a full TX ring buffer — the loop spins forever with no delay and no timeout, causing the task watchdog to fire (device reboot) or a permanent hang. Note the fwrite() path (USB-JTAG serial on ESP32-S3) never returns -1, so any short write/error there already spins forever today.

write_ble() (main/ble/ble.c:308-334) has the correct pattern: bounded retries (~2s) then return false. And jade_process_get_out_message() (main/process.c:467-469) documents that a failed write drops the message (the host times out and retries), so returning false is safe and recoverable.

Suggested fix

Mirror the BLE approach in write_serial(): retry with a bounded timeout (with a small delay between attempts), then return false after e.g. ~2s of consecutive zero-writes. The success path (wrote > 0) is unchanged; the only behavior change is on a stuck link, where dropping the message (host retry) replaces a watchdog reboot.

Verification

Confirmed still present on current master (as of 2026-08-04): main/serial.c:156 unchanged. The exact trigger needs hardware confirmation of the driver return value (0 vs -1) when the device is disconnected mid-write.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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