7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Wei Jie Law commit bfa09dccf98cf78dd9282259b2140bd1472f5ae9 upstream. For the 0x8513 chip, read_bulk_callback() skips the 2-byte receive header by advancing pegasus->rx_skb->data in place. This is done before the pkt_len > PEGASUS_MTU check, and when that check drops the frame the good path resubmits the same skb with the advanced rx_skb->data as the transfer buffer, still PEGASUS_MTU bytes long. Every frame with an oversized length field therefore moves the receive buffer two more bytes towards the end of the skb data area. A device that keeps sending such frames makes the host controller write received data over skb_shared_info and past the end of the allocation, and the corrupted skb_shared_info is used when the skb is eventually freed. With an emulated device sending frames filled with 0x5a on a KASAN kernel: BUG: KASAN: slab-out-of-bounds in dummy_timer+0xade/0x3f00 [dummy_hcd] Write of size 1536 at addr ffff888105821202 by task swapper/1/0 ... Oops: general protection fault, probably for non-canonical address 0xeb4b474b4b4b4b4c: 0000 [#1] PREEMPT SMP KASAN NOPTI KASAN: maybe wild-memory-access in range [0x5a5a5a5a5a5a5a60-0x5a5a5a5a5a5a5a67] RIP: 0010:skb_release_data+0x36f/0x800 Only skip the header with skb_reserve() once the frame is going to be passed up, so the receive buffer always starts at the beginning of the skb data area. Unlike the old pointer adjustment, skb_reserve() also moves skb->tail, so the two header bytes now count against the buffer size: lower the length limit for the 0x8513 accordingly, otherwise a pkt_len of PEGASUS_MTU - 1 or PEGASUS_MTU would trip skb_over_panic(). Found by code review with LLM assistance and reproduced with an emulated USB device (raw-gadget + dummy_hcd). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Wei Jie Law Reviewed-by: Simon Horman Link: https://patch.msgid.link/20261021020310.34045-2-law.wei.jie@gt.tech.gov.sg Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/pegasus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -461,6 +461,7 @@ static void read_bulk_callback(struct ur u8 *buf = urb->transfer_buffer; int rx_status, count = urb->actual_length; int status = urb->status; + int rx_offset = 0; __u16 pkt_len; if (!pegasus) @@ -510,7 +511,7 @@ static void read_bulk_callback(struct ur if (pegasus->chip == 0x8513) { pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); pkt_len &= 0x0fff; - pegasus->rx_skb->data += 2; + rx_offset = 2; } else { pkt_len = buf[count - 3] << 8; pkt_len += buf[count - 4]; @@ -522,13 +523,14 @@ static void read_bulk_callback(struct ur * If the packet is unreasonably long, quietly drop it rather than * kernel panicing by calling skb_put. */ - if (pkt_len > PEGASUS_MTU) + if (pkt_len > PEGASUS_MTU - rx_offset) goto goon; /* * at this point we are sure pegasus->rx_skb != NULL * so we go ahead and pass up the packet. */ + skb_reserve(pegasus->rx_skb, rx_offset); skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb);