kalmia_rx_fixup(): possible out of bounds read on a short RX frame

From: Maoyi Xie

Date: Mon Jun 15 2026 - 02:14:35 EST


Hi all,

I was reading the usbnet rx_fixup helpers after the recent endpoint validation
fixes. I think kalmia_rx_fixup() in drivers/net/usb/kalmia.c can read past the
receive buffer when a device sends a short frame. I am not sure about it, so I
would appreciate it if you could let me know whether you see this as a real
problem.

This is the relevant part of the loop:

if (skb->len < KALMIA_HEADER_LENGTH) /* 6 */
return 0;
...
usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); /* u16 */
ether_packet_length = get_unaligned_le16(&header_start[2]); /* from device */
skb_pull(skb, KALMIA_HEADER_LENGTH);

if (usb_packet_length < ether_packet_length) {
ether_packet_length = usb_packet_length + KALMIA_HEADER_LENGTH;
is_last = true;
} else {
is_last = (memcmp(skb->data + ether_packet_length,
HEADER_END_OF_USB_PACKET, ...) == 0);
...
}

The only length check is skb->len < KALMIA_HEADER_LENGTH, which is 6. Say a
device sends a frame with skb->len between 6 and 11. Then usb_packet_length is
skb->len minus 12. It is a u16, so it wraps to a large value. Now the check
usb_packet_length < ether_packet_length is false. So ether_packet_length is used
as is. It comes straight from the frame. It can be as large as 65535. It is then
used to index the memcmp() above. That memcmp reads past the end of the receive
buffer. The skb_trim() and skb_pull() after it use the same value. The buffer is
rx_urb_size, which is hard_mtu * 10, so 14000 bytes. CVE-2026-23365 added
endpoint checks to kalmia_bind(), but it does not cover this path.

I do not have the hardware. So I called the function directly with a crafted skb
on 7.1-rc7 with KASAN. The frame was skb->len 8, header 0x57 0x44, and
ether_packet_length 210. The read goes past the buffer and faults. The address
is the frame start plus 6 plus 210:

BUG: unable to handle page fault for address: ffffc900001a40d0
#PF: supervisor read access in kernel mode
RIP: 0010:kalmia_rx_fixup+0x9a/0x310
RAX: ffffc900001a40d0 R12: 00000000fffffffc R14: 00000000000000d2
CR2: ffffc900001a40d0

R14 is 0xd2, which is 210. That is the ether_packet_length from the frame. R12
is 0xfffffffc, which is skb->len minus 12 after it wrapped. So the short frame
made the length wrap. Then the value 210 from the device was used as the offset.

The smallest fix I could think of is to require both framing headers before the
subtraction, on every iteration:

if (skb->len < 2 * KALMIA_HEADER_LENGTH)
return 0;

I tried it. With that check the same frame is dropped and the fault is gone. I
am not sure how it fits with the "Some small packets misses end marker" case
just below, so I did not want to assume. I would appreciate it if you could let
me know whether this is a real issue, and whether that is the right place to fix
it. I am happy to send a proper patch with the reproducer once you confirm.

Thanks,
Maoyi
https://maoyixie.com/