From: "HE WEI (ギカク)" <skyexpoc@gmail.com>
To: Hans de Goede <hansg@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Andi Shyti <andi.shyti@kernel.org>
Cc: Sakari Ailus <sakari.ailus@linux.intel.com>,
linux-usb@vger.kernel.org, linux-i2c@vger.kernel.org,
linux-kernel@vger.kernel.org, HE WEI <skyexpoc@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH v2 2/3] i2c: usbio: reject bridges with undersized transfer buffers
Date: Sun, 26 Jul 2026 20:35:08 +0900 [thread overview]
Message-ID: <20260726113511.57596-3-skyexpoc@gmail.com> (raw)
In-Reply-To: <20260726113511.57596-1-skyexpoc@gmail.com>
usbio_i2c_read() and usbio_i2c_write() derive their per-transfer chunk
size from the bridge buffer sizes:
u16 rxchunk = i2c->rxbuf_len - I2C_RW_OVERHEAD;
u16 txchunk = i2c->txbuf_len - I2C_RW_OVERHEAD;
I2C_RW_OVERHEAD is sizeof(struct usbio_bulk_packet) +
sizeof(struct usbio_i2c_rw), i.e. a size_t of value 10, while the two
lengths are u16. The subtraction is therefore done in size_t and, for a
bridge reporting less than 10, wraps before being truncated back into a
u16. rxbuf_len = 8, a legal full speed bulk wMaxPacketSize, gives
rxchunk = 65534.
The chunk size decides whether a transfer has to be split:
if (msg->len > rxchunk) {
/* Need to split the input buffer */
The adapter quirks cap msg->len at 4096, so a wrapped chunk size makes
that condition permanently false, the splitting path becomes dead code,
and the single-shot path asks usbio_bulk_msg() for more than the bridge
buffer can hold.
The boundary value is worse. rxbuf_len of exactly 10 gives rxchunk 0,
and I2C_AQ_NO_ZERO_LEN guarantees msg->len is at least 1, so the split
loop is entered and never advances:
do {
if (msg->len - len < rxchunk)
rxchunk = msg->len - len;
ret = usbio_bulk_msg(...);
if (ret < 0)
return ret;
memcpy(&msg->buf[len], rbuf->data, rxchunk);
len += rxchunk;
} while (msg->len > len);
"msg->len - len < rxchunk" is 1 < 0, so rxchunk stays 0 and len never
grows. The only exit is an error return, so a device that keeps
answering keeps the loop running indefinitely and uninterruptibly, while
holding both usbio->bulk_mutex and the client mutex, which blocks every
other user of the bridge. txbuf_len is independent and can be left at a
normal value, so usbio_i2c_init() succeeds and the read is reached.
Check both lengths once at probe time.
This is a behaviour change: a bridge reporting a bulk wMaxPacketSize
below 11 no longer gets an I2C adapter at all. Such an adapter could
never have completed a transfer. usbio_i2c_xfer() runs usbio_i2c_init()
first and bails out on failure, and that needs obuf_len = 7 to fit in
txbuf_len - sizeof(struct usbio_bulk_packet), so anything below 12
already returned -EMSGSIZE for every single transfer. Failing to probe
is better than registering an adapter that cannot work. No supported
bridge is affected: they use 64, or 63 via USBIO_QUIRK_BULK_MAXP_63.
This additionally covers the case where usbio_get_txrxbuf_len() returns
without writing its output parameters because the bridge is already
gone; the lengths then stay 0 and the bridge is rejected instead of
being used.
Found by code review. The non-terminating loop was reproduced with a
userspace model of usbio_i2c_read()'s split path; it has not been
exercised on hardware or on dummy_hcd.
Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5 asan
Signed-off-by: HE WEI (ギカク) <skyexpoc@gmail.com>
---
--- a/drivers/i2c/busses/i2c-usbio.c
+++ b/drivers/i2c/busses/i2c-usbio.c
@@ -245,6 +245,20 @@
usbio_acpi_bind(i2c->adev, usbio_i2c_acpi_hids);
usbio_get_txrxbuf_len(i2c->adev, &i2c->txbuf_len, &i2c->rxbuf_len);
+
+ /*
+ * usbio_i2c_read() and usbio_i2c_write() compute their chunk size as
+ * "<buf>_len - I2C_RW_OVERHEAD". Those lengths are u16 and the
+ * overhead is a size_t, so a bridge reporting less than the overhead
+ * makes the subtraction wrap and the truncated u16 chunk size then
+ * defeats the splitting logic, while reporting exactly the overhead
+ * makes the chunk 0 and the split loop in usbio_i2c_read() never
+ * advance. Refuse to attach to such a bridge.
+ */
+ if (i2c->txbuf_len <= I2C_RW_OVERHEAD || i2c->rxbuf_len <= I2C_RW_OVERHEAD)
+ return dev_err_probe(dev, -EINVAL,
+ "Bridge buffers too small: tx %u rx %u\n",
+ i2c->txbuf_len, i2c->rxbuf_len);
i2c->rwbuf = devm_kzalloc(dev, max(i2c->txbuf_len, i2c->rxbuf_len), GFP_KERNEL);
if (!i2c->rwbuf)
--
2.51.0
next prev parent reply other threads:[~2026-07-26 11:36 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-26 11:35 [PATCH v2 0/3] usbio: fix two out-of-bounds accesses and a hang HE WEI (ギカク)
2026-07-26 11:35 ` [PATCH v2 1/3] usb: misc: usbio: reject endpoints smaller than the packet header HE WEI (ギカク)
2026-07-26 11:41 ` Greg Kroah-Hartman
2026-07-26 11:35 ` HE WEI (ギカク) [this message]
2026-07-26 11:35 ` [PATCH v2 3/3] usb: misc: usbio: bound the debug hex dumps by the received length HE WEI (ギカク)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260726113511.57596-3-skyexpoc@gmail.com \
--to=skyexpoc@gmail.com \
--cc=andi.shyti@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=hansg@kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=sakari.ailus@linux.intel.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox