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.

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