[PATCH 4/5] can: peak_usb: validate channel numbers in PCAN-USB FD
From: Stéphane Grosjean
Date: Thu Oct 08 2026 - 05:40:25 EST
From: Stéphane Grosjean <s.grosjean@xxxxxxxxxxxxxx>
The PCAN-USB FD family encodes the CAN channel number in messages
received from the device. This value is used as an index into the
adapter CAN device table.
Validate the channel number against the number of CAN controllers
supported by the adapter before performing the lookup. Any message
containing an invalid channel number is treated as malformed and the
entire message buffer is discarded, as the device is considered to be
providing untrusted data.
Additionally, return -EINVAL instead of -ENOMEM when an invalid
channel number is detected, making the error reporting consistent
with the rest of the driver.
This prevents potential out-of-bounds accesses when handling
unexpected or corrupted messages received from PCAN-USB FD family
devices.
Fixes: a6921dd524fe ("can: peak_usb: add range checking in decode operations")
Signed-off-by: Stéphane Grosjean <s.grosjean@xxxxxxxxxxxxxx>
---
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 32 ++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 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 82502594a409..ddbb2be1de0d 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -60,6 +60,7 @@ struct __packed pcan_ufd_fw_info {
/* handle device specific info used by the netdevices */
struct pcan_usb_fd_if {
+ const struct peak_usb_adapter *adapter;
struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL];
struct pcan_ufd_fw_info fw_info;
struct peak_time_ref time_ref;
@@ -536,8 +537,12 @@ static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
struct sk_buff *skb;
const u16 rx_msg_flags = le16_to_cpu(rm->flags);
- if (pucan_msg_get_channel(rm) >= ARRAY_SIZE(usb_if->dev))
- return -ENOMEM;
+ /* Reject invalid channel numbers reported by the firmware.
+ * Any channel below ctrl_count is guaranteed to have a valid
+ * entry in usb_if->dev[].
+ */
+ if (pucan_msg_get_channel(rm) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
dev = usb_if->dev[pucan_msg_get_channel(rm)];
netdev = dev->netdev;
@@ -605,8 +610,12 @@ static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
struct can_frame *cf;
struct sk_buff *skb;
- if (pucan_stmsg_get_channel(sm) >= ARRAY_SIZE(usb_if->dev))
- return -ENOMEM;
+ /* Reject invalid channel numbers reported by the firmware.
+ * Any channel below ctrl_count is guaranteed to have a valid
+ * entry in usb_if->dev[].
+ */
+ if (pucan_stmsg_get_channel(sm) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
dev = usb_if->dev[pucan_stmsg_get_channel(sm)];
pdev = container_of(dev, struct pcan_usb_fd_device, dev);
@@ -662,7 +671,11 @@ static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if,
struct pcan_usb_fd_device *pdev;
struct peak_usb_device *dev;
- if (pucan_ermsg_get_channel(er) >= ARRAY_SIZE(usb_if->dev))
+ /* Reject invalid channel numbers reported by the firmware.
+ * Any channel below ctrl_count is guaranteed to have a valid
+ * entry in usb_if->dev[].
+ */
+ if (pucan_ermsg_get_channel(er) >= usb_if->adapter->ctrl_count)
return -EINVAL;
dev = usb_if->dev[pucan_ermsg_get_channel(er)];
@@ -685,7 +698,11 @@ static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if,
struct can_frame *cf;
struct sk_buff *skb;
- if (pufd_omsg_get_channel(ov) >= ARRAY_SIZE(usb_if->dev))
+ /* Reject invalid channel numbers reported by the firmware.
+ * Any channel below ctrl_count is guaranteed to have a valid
+ * entry in usb_if->dev[].
+ */
+ if (pufd_omsg_get_channel(ov) >= usb_if->adapter->ctrl_count)
return -EINVAL;
dev = usb_if->dev[pufd_omsg_get_channel(ov)];
@@ -987,6 +1004,9 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev)
if (!pdev->cmd_buffer_addr)
goto err_out_1;
+ /* keep reference to the adapter device */
+ pdev->usb_if->adapter = dev->adapter;
+
/* number of ts msgs to ignore before taking one into account */
pdev->usb_if->cm_ignore_count = 5;
--
2.43.0