From e308f7475155c303a833081dc3448b7e2759c809 Mon Sep 17 00:00:00 2001 From: snokvist Date: Fri, 30 Oct 2026 17:32:53 +0100 Subject: [PATCH] mt7612u: a failed bulk submit reported the previous transfer's length sync_xfer_new() recycles libusb transfers from a per-device pool, and a recycled transfer keeps its last actual_length: libusb writes the field only when a transfer completes, never when libusb_submit_transfer() fails. submit_and_wait() returns a submit failure directly and sync_bulk() then reads actual_length anyway - so once the device has left the bus, an IN read reported the previous transfer's length and memcpy'd that many bytes from its len-byte bounce buffer into the caller's buffer. Reproduced on an MT7612U (8-1): one 1400 B frame through mt7612u_tx() (sync bulk OUT, 1428 B on the wire), the adapter deauthorized in sysfs, then mt_bulk() on EP 0x85 with a 64 B length into a sentinel-filled buffer. Upstream master: rc LIBUSB_ERROR_IO, n = 1428, 1364 bytes written past the 64 B buffer. With this change: rc LIBUSB_ERROR_IO, n = 0, none. The worst real case is a TX of up to MT_TX_BUF_MAX (4096 B), an unplug, then an MCU reply read into its 1024 B stack buffer. sync_control() reads actual_length only on rc == 0 and was not affected; the async rings use their own transfers and read it only in completion callbacks. A physical unplug fails the submit with NO_DEVICE rather than IO; both take the same path. Introduced with the pool in b9b80de (#444). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011dReg1L6xtBhf95NpAeuge --- src/mt7612u/usb.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mt7612u/usb.cpp b/src/mt7612u/usb.cpp index de245a22..c68f5dd9 100644 --- a/src/mt7612u/usb.cpp +++ b/src/mt7612u/usb.cpp @@ -217,6 +217,14 @@ struct sync_xfer *sync_xfer_new(struct mt7612u_dev *d, size_t len) delete x; return nullptr; } + /* A pooled transfer keeps its last actual_length, and libusb writes the + * field only when a transfer completes - not when libusb_submit_transfer() + * fails, as it does once the device has left the bus. sync_bulk() reads + * it on every return but an abandon, so after a failed submit an IN read + * reported the previous transfer's length and copied that many bytes from + * its len-byte bounce buffer into the caller's: a 1428 B TX, the adapter + * deauthorized, then a 64 B read wrote 1364 B past the end. */ + x->t->actual_length = 0; return x; }