[PATCH net 2/2] net: usb: lan78xx: reject short register and statistics reads
From: Wei Jie Law
Date: Tue Oct 13 2026 - 23:02:23 EST
lan78xx_read_reg() and lan78xx_read_stats() read from the device into
a freshly kmalloc()ed buffer, but only treat a negative return value
from usb_control_msg() as an error. A device that answers with fewer
bytes than requested leaves part or all of the buffer uninitialized,
and those bytes are then used as the register value or the counters.
With an emulated device on a kernel booted with init_on_alloc=0:
- A zero-length reply to the statistics request makes ethtool -S, which
needs no privileges, report the stale contents of the kmalloc-192
buffer. One counter pair read back as 0xffffffff8a427800, the
address of usb_api_blocking_completion(), next to several direct
map pointers.
- A zero-length reply to the RX_ADDRL/RX_ADDRH reads in
lan78xx_init_mac_address() turns uninitialized heap memory into the
MAC address (12:85:1f:dc:2e:bd and e6:93:44:41:9b:9f on two probes),
which is then written back to the device in RX_ADDRL/RX_ADDRH and
MAF_LO(0)/MAF_HI(0).
Treat a short transfer as -EIO in both helpers. With this change the
same device fails to probe with -EIO, and with only the statistics
reply cut short all counters stay zero.
Found by code review with LLM assistance and reproduced with an
emulated USB device (raw-gadget + dummy_hcd).
Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Wei Jie Law <law.wei.jie@xxxxxxxxxxxxxx>
---
drivers/net/usb/lan78xx.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index cb8204371f5c..e6be066349de 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -612,6 +612,9 @@ static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
USB_VENDOR_REQUEST_READ_REGISTER,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, buf, 4, USB_CTRL_GET_TIMEOUT);
+ if (ret >= 0 && ret != sizeof(*buf))
+ ret = -EIO;
+
if (likely(ret >= 0)) {
le32_to_cpus(buf);
*data = *buf;
@@ -695,6 +698,9 @@ static int lan78xx_read_stats(struct lan78xx_net *dev,
(void *)stats,
sizeof(*stats),
USB_CTRL_SET_TIMEOUT);
+ if (ret >= 0 && ret != sizeof(*stats))
+ ret = -EIO;
+
if (likely(ret >= 0)) {
src = (u32 *)stats;
dst = (u32 *)data;
--
2.43.0