[PATCH 1/2] can: peak_usb: validate URB length in pcan_usb_fd_decode_buf()

From: Berkant Koc

Date: Sun May 17 2026 - 10:16:51 EST


pcan_usb_fd_decode_buf() walks records inside the bulk-in URB by reading
the 12-byte struct pucan_msg header from the front of each record. The
existing loop only verifies that msg_ptr is below msg_end before
dereferencing rx_msg->size and rx_msg->type, which means a short URB
that contains between 1 and 11 bytes of payload causes a two-byte
out-of-bounds read of the rx_msg->size and rx_msg->type fields. The
fragment check that follows compares the announced size against msg_end
but lands after the header has already been read.

A malicious USB device that pretends to be a PEAK-System PCAN-USB-FD
adapter (USB IDs 0c72:0012, 0c72:0014, 0c72:0016) can keep returning
short bulk-in URBs and trigger this read on every poll cycle, leaking
adjacent slab content via the dispatched decode paths or simply
producing a KASAN slab-out-of-bounds report.

Apply the pattern from commit 6fe9f3279f7d ("can: gs_usb: gs_usb_receive_bulk_callback(): check actual_length before accessing header"):
require that at least sizeof(struct pucan_msg) bytes remain before each
iteration, and reject records whose announced size is smaller than the
header itself.

Identified by static analysis. No KASAN trip available without specific
PEAK CAN-FD hardware.

Fixes: 0a25e1f4f185 ("can: peak_usb: add support for PEAK new CANFD USB adapters")
Cc: stable@xxxxxxxxxxxxxxx # 4.0+
Signed-off-by: Berkant Koc <me@xxxxxxxxxx>
---
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
index eb4f5884ad73..63d93f90165c 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -726,7 +726,7 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
/* loop reading all the records from the incoming message */
msg_ptr = urb->transfer_buffer;
msg_end = urb->transfer_buffer + urb->actual_length;
- for (; msg_ptr < msg_end;) {
+ while (msg_ptr + sizeof(struct pucan_msg) <= msg_end) {
u16 rx_msg_type, rx_msg_size;

rx_msg = (struct pucan_msg *)msg_ptr;
@@ -738,8 +738,9 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
rx_msg_size = le16_to_cpu(rx_msg->size);
rx_msg_type = le16_to_cpu(rx_msg->type);

- /* check if the record goes out of current packet */
- if (msg_ptr + rx_msg_size > msg_end) {
+ /* check if the record fits inside the current packet */
+ if (rx_msg_size < sizeof(struct pucan_msg) ||
+ msg_ptr + rx_msg_size > msg_end) {
netdev_err(netdev,
"got frag rec: should inc usb rx buf sze\n");
err = -EBADMSG;
--
2.47.3