[BUG] pegasus: RX length confusion and Pegasus-II heap corruption
From: Jurre van Bergen <hidden>
Date: 2026-09-14 19:25:43
Also in:
linux-usb
Hello, I would like to report two issues in drivers/net/usb/pegasus.c, read_bulk_callback(). A malicious USB device that emulates as a supported Pegasus adapter can trigger the two issues. First issue, the driver trusts the packet length stored in data supplied by the USB device. It checks the length against PEGASUS_MTU, but does not check that the claimed packet is present in urb->actual_length. A short USB transfer can therefore make skb_put() expose unwritten skb tailroom to the network stack. The device can receive some of this kernel data through ICMP error responses. Second issue, on Pegasus-II (chip 0x8513), the driver advances rx_skb->data by two bytes before it rejects an oversized packet. The rejected skb is then reused as the destination of the next 1536-byte RX URB. Repeated rejected packets move the destination forward through the allocation. This allows cross-object reads and a repeatable, device-controlled linear overwrite of skb_shared_info and later heap memory. RX-error packets can keep the destination at the chosen offset while writing new data. The important code pattern is:
if (pegasus->chip == 0x8513) {
/* Length comes from the USB device. */
pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer) & 0x0fff;
pegasus->rx_skb->data += 2;
}
if (pkt_len > PEGASUS_MTU)
goto goon; /* skb is reused with shifted data */
skb_put(pegasus->rx_skb, pkt_len); /* no actual_length check */
goon:
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, pipe,
pegasus->rx_skb->data, PEGASUS_MTU,
read_bulk_callback, pegasus);
The issues were reproduced with raw_gadget and dummy_hcd using the built-in ADM8513 ID (07a6:8513) as emulation target. The Pegasus-II overwrite is confirmed exploitable: https://x.com/DrWhax/status/2098408939507351760 Possible fix directions: - Convert the device length into an Ethernet frame length. Reject it if it is smaller than ETH_HLEN, larger than PEGASUS_MTU, or larger than the bytes present after the device-specific prefix and trailer. Check minimum wire lengths before subtraction to avoid integer underflow. - Remove the direct rx_skb->data adjustment. After validation succeeds, use skb_put(), skb_pull(), and skb_trim(), or an equivalent sequence that keeps data, tail, and len consistent. - Make every rejected-packet path leave the skb unchanged, so the next RX URB always uses the original buffer address. If this cannot be guaranteed, discard the skb and allocate a fresh one. - Before each URB submission, verify that PEGASUS_MTU bytes fit between the exact transfer-buffer address and skb_end_pointer(). - Read the Pegasus-II length with get_unaligned_le32(). Groetjes, Jurre van Bergen