[PATCH net v2 2/2] net: usb: pegasus: check rx pkt_len against the received length
From: Wei Jie Law
Date: Tue Oct 20 2026 - 22:05:12 EST
read_bulk_callback() takes the frame length from the device, from the
receive header on the 0x8513 and from the trailing status bytes on the
other chips, and only checks it against PEGASUS_MTU before passing that
many bytes up the stack. In two cases the stack then uses bytes that the
device did not send:
- A device that sends a short transfer but claims a long frame gets the
stale or uninitialized remainder of the receive skb delivered as
packet data.
- A device that claims a frame shorter than an Ethernet header gets
past the check as well. eth_type_trans() takes pkt_type from h_dest
and the protocol from h_proto without checking skb->len. If the
transfer is also short, those come from bytes left in the skb by an
earlier transfer, or from uninitialized memory.
Drop frames shorter than ETH_HLEN, and frames whose claimed length, plus
the 0x8513 header, does not fit in the bytes actually received, and
count them as length errors. urb->actual_length never exceeds the
PEGASUS_MTU sized transfer buffer, so this also covers the existing
PEGASUS_MTU check. Valid Ethernet frames are at least 60 bytes long
without the FCS, so they are not affected.
Found by code review with LLM assistance. With an emulated USB device,
a 101-byte transfer claiming a 1500-byte frame was passed up as a
1500-byte packet before this change and is dropped after it, and
frames claiming 0, 4 and 13 bytes were passed up and are now dropped
and counted in rx_length_errors. The extra bytes happened to be zero
in the first test, so disclosure of actual kernel data was not
demonstrated.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Wei Jie Law <law.wei.jie@xxxxxxxxxxxxxx>
---
drivers/net/usb/pegasus.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index 7c7d6b69998f..68e4b6658dd2 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -520,11 +520,17 @@ static void read_bulk_callback(struct urb *urb)
}
/*
- * If the packet is unreasonably long, quietly drop it rather than
- * kernel panicing by calling skb_put.
+ * Drop frames shorter than an Ethernet header, because
+ * eth_type_trans() reads the header whatever the length, and frames
+ * longer than what was actually received. Passing either up would
+ * expose stale buffer contents, and skb_put() could panic on the
+ * latter. count is at most PEGASUS_MTU.
*/
- if (pkt_len > PEGASUS_MTU - rx_offset)
+ if (pkt_len < ETH_HLEN || pkt_len + rx_offset > count) {
+ net->stats.rx_errors++;
+ net->stats.rx_length_errors++;
goto goon;
+ }
/*
* at this point we are sure pegasus->rx_skb != NULL
--
2.43.0