[PATCH] can: esd_usb: validate receive message extents

From: Pengpeng Hou

Date: Sun Aug 30 2026 - 08:57:00 EST


The bulk callback reads the message command before proving that the current
URB remainder contains a header and dispatches CAN messages before checking
the declared message span. Short records can therefore be consumed before
the later position check.

Validate the common header, declared record extent, and command-specific
payload before dispatch.

Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device")
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
drivers/net/can/usb/esd_usb.c | 46 +++++++++++++++++++++++++++++++++++++------
1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index f41d4a0d140f7..1112af5d41d9c 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -482,6 +482,33 @@ static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv,
netif_wake_queue(netdev);
}

+static bool esd_usb_rx_msg_valid(const union esd_usb_msg *msg, size_t len)
+{
+ size_t data_offset = offsetof(struct esd_usb_rx_msg, data);
+ u32 id;
+ u8 data_len;
+
+ if (msg->hdr.cmd == ESD_USB_CMD_CAN_TX)
+ return len >= sizeof(struct esd_usb_tx_done_msg);
+ if (msg->hdr.cmd != ESD_USB_CMD_CAN_RX)
+ return true;
+ if (len < data_offset)
+ return false;
+
+ id = le32_to_cpu(msg->rx.id);
+ if (id & ESD_USB_EVENT)
+ return len >= data_offset + sizeof(msg->rx.ev_can_err_ext);
+
+ if (msg->rx.dlc & ESD_USB_FD)
+ data_len = can_fd_dlc2len(msg->rx.dlc);
+ else if (msg->rx.dlc & ESD_USB_RTR)
+ data_len = 0;
+ else
+ data_len = can_cc_dlc2len(msg->rx.dlc);
+
+ return len >= data_offset + data_len;
+}
+
static void esd_usb_read_bulk_callback(struct urb *urb)
{
struct esd_usb *dev = urb->context;
@@ -507,8 +534,20 @@ static void esd_usb_read_bulk_callback(struct urb *urb)

while (pos < urb->actual_length) {
union esd_usb_msg *msg;
+ size_t msg_len;
+ int remaining = urb->actual_length - pos;

+ if (remaining < sizeof(msg->hdr)) {
+ dev_err(dev->udev->dev.parent, "format error\n");
+ break;
+ }
msg = (union esd_usb_msg *)(urb->transfer_buffer + pos);
+ msg_len = msg->hdr.len * sizeof(u32);
+ if (msg_len < sizeof(msg->hdr) || msg_len > remaining ||
+ !esd_usb_rx_msg_valid(msg, msg_len)) {
+ dev_err(dev->udev->dev.parent, "format error\n");
+ break;
+ }

switch (msg->hdr.cmd) {
case ESD_USB_CMD_CAN_RX:
@@ -531,12 +570,7 @@ static void esd_usb_read_bulk_callback(struct urb *urb)
break;
}

- pos += msg->hdr.len * sizeof(u32); /* convert to # of bytes */
-
- if (pos > urb->actual_length) {
- dev_err(dev->udev->dev.parent, "format error\n");
- break;
- }
+ pos += msg_len;
}

resubmit_urb:

base-commit: 08dbfad3f5040f5bdb6c529da20d6d4e81fefd72
--
2.50.1