* [PATCH v5 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 02/10] can: peak_usb: add missing includes Stéphane Grosjean via B4 Relay
` (9 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel,
Stefan Günther, stable
From: Stefan Günther <stefangun@pm.me>
When the device reports error counters, the driver incorrectly uses an
assignment (=) instead of a bitwise OR (|=) for CAN_ERR_CNT. This wipes
out the CAN_ERR_FLAG and CAN_ERR_CRTL flags, causing the error frame to
be sent to userspace as a regular CAN frame with ID 0x200.
Fix this by using a bitwise OR to preserve the flags.
Signed-off-by: Stefan Günther <stefangun@pm.me>
Cc: stable@vger.kernel.org
Fixes: 3e5c291c7942 ("can: add CAN_ERR_CNT flag to notify availability of error counter")
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 8fd058c32856..785224e00c4e 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -526,7 +526,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
/* Supply TX/RX error counters in case of
* controller error.
*/
- cf->can_id = CAN_ERR_CNT;
+ cf->can_id |= CAN_ERR_CNT;
cf->data[6] = mc->pdev->bec.txerr;
cf->data[7] = mc->pdev->bec.rxerr;
}
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 02/10] can: peak_usb: add missing includes
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 03/10] can: peak_usb: sort ctrlmode_supported flags Stéphane Grosjean via B4 Relay
` (8 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Marc Kleine-Budde <mkl@pengutronix.de>
To make the header self contained, so that LSP servers don't throw errors,
make the header file self contained and include all needed header files.
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
drivers/net/can/usb/peak_usb/pcan_usb_core.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index 65999f04f4b7..9ad7d31d2720 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -11,6 +11,12 @@
#ifndef PCAN_USB_CORE_H
#define PCAN_USB_CORE_H
+#include <linux/can/dev.h>
+#include <linux/can/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+
/* PEAK-System vendor id. */
#define PCAN_USB_VENDOR_ID 0x0c72
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 03/10] can: peak_usb: sort ctrlmode_supported flags
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 02/10] can: peak_usb: add missing includes Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 04/10] can: peak_usb: harden PCAN-USB message validation Stéphane Grosjean via B4 Relay
` (7 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
Order the CAN_CTRLMODE_* flags in ctrlmode_supported according to
their numerical values.
This is a cosmetic change only and does not alter the driver
behavior.
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 3 ++-
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 32 ++++++++++++++++++-----------
drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 3 ++-
3 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 785224e00c4e..169c00d93463 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -1016,7 +1016,8 @@ const struct peak_usb_adapter pcan_usb = {
.name = "PCAN-USB",
.device_id = PCAN_USB_PRODUCT_ID,
.ctrl_count = 1,
- .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
CAN_CTRLMODE_CC_LEN8_DLC,
.clock = {
.freq = PCAN_USB_CRYSTAL_HZ / 2,
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 0d46f4ce5dca..82502594a409 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -1198,9 +1198,11 @@ const struct peak_usb_adapter pcan_usb_fd = {
.name = "PCAN-USB FD",
.device_id = PCAN_USBFD_PRODUCT_ID,
.ctrl_count = PCAN_USBFD_CHANNEL_COUNT,
- .ctrlmode_supported = CAN_CTRLMODE_FD |
- CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
- CAN_CTRLMODE_ONE_SHOT | CAN_CTRLMODE_CC_LEN8_DLC,
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_ONE_SHOT |
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_CC_LEN8_DLC,
.clock = {
.freq = PCAN_UFD_CRYSTAL_HZ,
},
@@ -1274,9 +1276,11 @@ const struct peak_usb_adapter pcan_usb_chip = {
.name = "PCAN-Chip USB",
.device_id = PCAN_USBCHIP_PRODUCT_ID,
.ctrl_count = PCAN_USBFD_CHANNEL_COUNT,
- .ctrlmode_supported = CAN_CTRLMODE_FD |
- CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
- CAN_CTRLMODE_ONE_SHOT | CAN_CTRLMODE_CC_LEN8_DLC,
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_ONE_SHOT |
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_CC_LEN8_DLC,
.clock = {
.freq = PCAN_UFD_CRYSTAL_HZ,
},
@@ -1350,9 +1354,11 @@ const struct peak_usb_adapter pcan_usb_pro_fd = {
.name = "PCAN-USB Pro FD",
.device_id = PCAN_USBPROFD_PRODUCT_ID,
.ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
- .ctrlmode_supported = CAN_CTRLMODE_FD |
- CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
- CAN_CTRLMODE_ONE_SHOT | CAN_CTRLMODE_CC_LEN8_DLC,
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_ONE_SHOT |
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_CC_LEN8_DLC,
.clock = {
.freq = PCAN_UFD_CRYSTAL_HZ,
},
@@ -1426,9 +1432,11 @@ const struct peak_usb_adapter pcan_usb_x6 = {
.name = "PCAN-USB X6",
.device_id = PCAN_USBX6_PRODUCT_ID,
.ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT,
- .ctrlmode_supported = CAN_CTRLMODE_FD |
- CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
- CAN_CTRLMODE_ONE_SHOT | CAN_CTRLMODE_CC_LEN8_DLC,
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_ONE_SHOT |
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_CC_LEN8_DLC,
.clock = {
.freq = PCAN_UFD_CRYSTAL_HZ,
},
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index b6be8c19e537..4c3d89a160af 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -1072,7 +1072,8 @@ const struct peak_usb_adapter pcan_usb_pro = {
.name = "PCAN-USB Pro",
.device_id = PCAN_USBPRO_PRODUCT_ID,
.ctrl_count = PCAN_USBPRO_CHANNEL_COUNT,
- .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
+ .ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_3_SAMPLES |
CAN_CTRLMODE_ONE_SHOT,
.clock = {
.freq = PCAN_USBPRO_CRYSTAL_HZ,
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 04/10] can: peak_usb: harden PCAN-USB message validation
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (2 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 03/10] can: peak_usb: sort ctrlmode_supported flags Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 05/10] can: peak_usb: replace open-coded bitmask operations with bitops Stéphane Grosjean via B4 Relay
` (6 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
Add boundary checks when parsing PCAN-USB messages and reject
malformed buffers whose contents would otherwise lead to accesses
outside the received USB data area.
This prevents potential out-of-bounds memory accesses caused by
corrupted or malicious device messages.
Fixes: 46be265d3388 ("can: usb: PEAK-System Technik PCAN-USB specific part")
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 169c00d93463..5da91d5dc67a 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -558,6 +558,8 @@ static int pcan_usb_handle_bus_evt(struct pcan_usb_msg_context *mc, u8 ir)
switch (ir) {
case PCAN_USB_ERR_CNT_DEC:
case PCAN_USB_ERR_CNT_INC:
+ if ((mc->ptr + 3) > mc->end)
+ return -EINVAL;
/* save rx/tx error counters from in the device context */
pdev->bec.rxerr = mc->ptr[1];
@@ -696,8 +698,11 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len)
mc->ptr += rec_len;
/* Ignore next byte (client private id) if SRR bit is set */
- if (can_id_flags & PCAN_USB_TX_SRR)
+ if (can_id_flags & PCAN_USB_TX_SRR) {
+ if ((mc->ptr + 1) > mc->end)
+ goto decode_failed;
mc->ptr++;
+ }
/* update statistics */
mc->netdev->stats.rx_bytes += cf->len;
@@ -733,14 +738,19 @@ static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf)
int err;
for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) {
- u8 sl = *mc.ptr++;
-
- /* handle status and error frames here */
- if (sl & PCAN_USB_STATUSLEN_INTERNAL) {
- err = pcan_usb_decode_status(&mc, sl);
- /* handle normal can frames here */
+ /* check if status_len byte can be read next */
+ if (mc.ptr >= mc.end) {
+ err = -EINVAL;
} else {
- err = pcan_usb_decode_data(&mc, sl);
+ u8 sl = *mc.ptr++;
+
+ /* handle status and error frames here */
+ if (sl & PCAN_USB_STATUSLEN_INTERNAL) {
+ err = pcan_usb_decode_status(&mc, sl);
+ /* handle normal can frames here */
+ } else {
+ err = pcan_usb_decode_data(&mc, sl);
+ }
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 05/10] can: peak_usb: replace open-coded bitmask operations with bitops
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (3 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 04/10] can: peak_usb: harden PCAN-USB message validation Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean via B4 Relay
` (5 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
The PCAN_USB_STATE_* flags of struct peak_usb_device are set, cleared
and tested from different contexts (ndo_open/ndo_stop, USB disconnect,
URB completion) with non-atomic open-coded bitmask operations.
Turn these flags into bit numbers, make the state field an unsigned
long and use set_bit(), clear_bit() and test_bit() instead, so that
updating one flag can no longer overwrite a concurrent update of
another one. The next patch relies on this to clear CONNECTED while a
channel may be opened.
dev->state is zeroed by alloc_candev(), so setting CONNECTED with
set_bit() at creation is equivalent to the former assignment.
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 4 ++--
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 10 +++++-----
drivers/net/can/usb/peak_usb/pcan_usb_core.h | 9 ++++++---
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 2 +-
drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 6 +++---
5 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 5da91d5dc67a..93da0fd4f10e 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -158,7 +158,7 @@ static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
int actual_length;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
dev->cmd_buf[PCAN_USB_CMD_FUNC] = f;
@@ -188,7 +188,7 @@ static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p)
int actual_length;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
/* first, send command */
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 55aad01cd8ca..a1b17e1e6fea 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -263,7 +263,7 @@ static void peak_usb_read_bulk_callback(struct urb *urb)
/* protect from any incoming empty msgs */
if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) {
/* handle these kinds of msgs only if _start callback called */
- if (dev->state & PCAN_USB_STATE_STARTED) {
+ if (test_bit(PCAN_USB_STATE_STARTED, &dev->state)) {
err = dev->adapter->dev_decode_buf(dev, urb);
if (err)
pcan_dump_mem("received usb message",
@@ -537,7 +537,7 @@ static int peak_usb_start(struct peak_usb_device *dev)
goto err_adapter;
}
- dev->state |= PCAN_USB_STATE_STARTED;
+ set_bit(PCAN_USB_STATE_STARTED, &dev->state);
/* can set bus on now */
if (dev->adapter->dev_set_bus) {
@@ -631,7 +631,7 @@ static int peak_usb_ndo_stop(struct net_device *netdev)
{
struct peak_usb_device *dev = netdev_priv(netdev);
- dev->state &= ~PCAN_USB_STATE_STARTED;
+ clear_bit(PCAN_USB_STATE_STARTED, &dev->state);
netif_stop_queue(netdev);
close_candev(netdev);
@@ -941,7 +941,7 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
dev->netdev = netdev;
dev->adapter = peak_usb_adapter;
dev->ctrl_idx = ctrl_idx;
- dev->state = PCAN_USB_STATE_CONNECTED;
+ set_bit(PCAN_USB_STATE_CONNECTED, &dev->state);
dev->ep_msg_in = peak_usb_adapter->ep_msg_in;
dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx];
@@ -1044,7 +1044,7 @@ static void peak_usb_disconnect(struct usb_interface *intf)
char name[IFNAMSIZ];
dev_prev_siblings = dev->prev_siblings;
- dev->state &= ~PCAN_USB_STATE_CONNECTED;
+ clear_bit(PCAN_USB_STATE_CONNECTED, &dev->state);
strscpy(name, netdev->name, IFNAMSIZ);
unregister_candev(netdev);
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index 9ad7d31d2720..cb8777c3e08e 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -109,15 +109,18 @@ struct peak_tx_urb_context {
struct urb *urb;
};
-#define PCAN_USB_STATE_CONNECTED 0x00000001
-#define PCAN_USB_STATE_STARTED 0x00000002
+/* bit numbers for struct peak_usb_device::state (used with set_bit()/
+ * clear_bit()/test_bit() for atomic read-modify-write across contexts)
+ */
+#define PCAN_USB_STATE_CONNECTED 0
+#define PCAN_USB_STATE_STARTED 1
/* PEAK-System USB device */
struct peak_usb_device {
struct can_priv can;
const struct peak_usb_adapter *adapter;
unsigned int ctrl_idx;
- u32 state;
+ unsigned long state;
struct usb_device *udev;
struct net_device *netdev;
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..15d275d6a127 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -202,7 +202,7 @@ static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
ptrdiff_t cmd_len;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
/* if a packet is not filled completely by commands, the command list
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index 4c3d89a160af..cbc23953ce61 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -210,7 +210,7 @@ static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev,
int err;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
err = usb_bulk_msg(dev->udev,
@@ -234,7 +234,7 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev,
int i, err = 0;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
req_data_type = pum->u.rec_buffer[4];
@@ -318,7 +318,7 @@ int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id,
unsigned int p;
/* usb device unregistered? */
- if (!(dev->state & PCAN_USB_STATE_CONNECTED))
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER;
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 06/10] can: peak_usb: harden channel handling in PCAN-USB FD
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (4 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 05/10] can: peak_usb: replace open-coded bitmask operations with bitops Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-20 13:30 ` [PATCH v5 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean via B4 Relay
` (4 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
Records received from PCAN-USB FD family devices carry a CAN channel
number used to index the adapter CAN device table usb_if->dev[].
The decoders only checked this number against the size of the table
(2 entries). On single-channel adapters (PCAN-USB FD, PCAN-Chip USB),
dev[1] is never set, so a record for channel 1 led to a NULL pointer
dereference in URB completion context. On two-channel adapters, a
channel slot is also NULL until that channel has been initialized,
while the RX URBs of channel 0 may already be running.
Check the channel number against the number of controllers of the
adapter and reject records for a channel whose slot is empty. Such a
record makes the decoder stop processing the rest of the URB.
When probing a channel fails after its slot has been filled, the
channel is freed but its slot kept pointing to it. Clear the slot in
pcan_usb_fd_free() and, before freeing the channel, stop RX on the
sibling channels already created, since the whole interface is then
released anyway. Slots are published with smp_store_release() and
read with READ_ONCE().
Finally, peak_usb_disconnect() now stops RX on all channels before
unregistering any of them: it clears CONNECTED, which makes
peak_usb_ndo_open() fail, then poisons the RX anchors. This matters
when it is called from the probe error path, where the USB core has
not disabled the interface.
Fixes: a6921dd524fe ("can: peak_usb: add range checking in decode operations")
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 30 +++++++++++++-
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 62 +++++++++++++++++++++-------
2 files changed, 75 insertions(+), 17 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index a1b17e1e6fea..4c08656e7768 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -574,6 +574,9 @@ static int peak_usb_ndo_open(struct net_device *netdev)
struct peak_usb_device *dev = netdev_priv(netdev);
int err;
+ if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
+ return -ENODEV;
+
/* common open */
err = open_candev(netdev);
if (err)
@@ -904,6 +907,22 @@ int pcan_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info
return 0;
}
+/*
+ * stop RX on dev and on all its previous siblings: clearing CONNECTED makes
+ * peak_usb_ndo_open() fail, then poisoning the RX anchors waits for running
+ * completion handlers and makes any later submission on them fail.
+ */
+static void peak_usb_stop_rx(struct peak_usb_device *dev)
+{
+ struct peak_usb_device *d;
+
+ for (d = dev; d; d = d->prev_siblings)
+ clear_bit(PCAN_USB_STATE_CONNECTED, &d->state);
+
+ for (d = dev; d; d = d->prev_siblings)
+ usb_poison_anchored_urbs(&d->rx_submitted);
+}
+
/*
* create one device which is attached to CAN controller #ctrl_idx of the
* usb adapter.
@@ -1014,6 +1033,12 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
return 0;
adap_dev_free:
+ /* the whole interface is released when probing a channel fails:
+ * stop RX on the channels already created, since their RX URBs may
+ * carry records for this one.
+ */
+ peak_usb_stop_rx(dev->prev_siblings);
+
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
@@ -1038,13 +1063,14 @@ static void peak_usb_disconnect(struct usb_interface *intf)
struct peak_usb_device *dev;
struct peak_usb_device *dev_prev_siblings;
- /* unregister as many netdev devices as siblings */
+ /* stop RX on all channels before unregistering and freeing them */
+ peak_usb_stop_rx(usb_get_intfdata(intf));
+
for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) {
struct net_device *netdev = dev->netdev;
char name[IFNAMSIZ];
dev_prev_siblings = dev->prev_siblings;
- clear_bit(PCAN_USB_STATE_CONNECTED, &dev->state);
strscpy(name, netdev->name, IFNAMSIZ);
unregister_candev(netdev);
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 15d275d6a127..eee5b5eaa247 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,10 +537,16 @@ 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 */
+ if (pucan_msg_get_channel(rm) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
+
+ dev = READ_ONCE(usb_if->dev[pucan_msg_get_channel(rm)]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
+ return -EINVAL;
- dev = usb_if->dev[pucan_msg_get_channel(rm)];
netdev = dev->netdev;
if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) {
@@ -605,10 +612,16 @@ 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 */
+ if (pucan_stmsg_get_channel(sm) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
+
+ dev = READ_ONCE(usb_if->dev[pucan_stmsg_get_channel(sm)]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
+ return -EINVAL;
- dev = usb_if->dev[pucan_stmsg_get_channel(sm)];
pdev = container_of(dev, struct pcan_usb_fd_device, dev);
netdev = dev->netdev;
@@ -662,10 +675,16 @@ 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 */
+ if (pucan_ermsg_get_channel(er) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
+
+ dev = READ_ONCE(usb_if->dev[pucan_ermsg_get_channel(er)]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
return -EINVAL;
- dev = usb_if->dev[pucan_ermsg_get_channel(er)];
pdev = container_of(dev, struct pcan_usb_fd_device, dev);
/* keep a trace of tx and rx error counters for later use */
@@ -685,10 +704,16 @@ 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 */
+ if (pufd_omsg_get_channel(ov) >= usb_if->adapter->ctrl_count)
+ return -EINVAL;
+
+ dev = READ_ONCE(usb_if->dev[pufd_omsg_get_channel(ov)]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
return -EINVAL;
- dev = usb_if->dev[pufd_omsg_get_channel(ov)];
netdev = dev->netdev;
/* allocate an skb to store the error frame */
@@ -981,6 +1006,9 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev)
if (!pdev->usb_if)
goto err_out;
+ /* needed by the decoders to check channel numbers */
+ pdev->usb_if->adapter = dev->adapter;
+
/* allocate command buffer once for all for the interface */
pdev->cmd_buffer_addr = kzalloc(PCAN_UFD_CMD_BUFFER_SIZE,
GFP_KERNEL);
@@ -1054,7 +1082,8 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev)
fw_info = &pdev->usb_if->fw_info;
}
- pdev->usb_if->dev[dev->ctrl_idx] = dev;
+ /* publish dev to the decoders, after the ctrlmode copies above */
+ smp_store_release(&pdev->usb_if->dev[dev->ctrl_idx], dev);
dev->can_channel_id =
le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]);
@@ -1124,14 +1153,17 @@ static void pcan_usb_fd_exit(struct peak_usb_device *dev)
}
}
-/* called when the USB adapter is unplugged */
+/* called when the USB adapter is unplugged, or when probing a sibling fails */
static void pcan_usb_fd_free(struct peak_usb_device *dev)
{
+ struct pcan_usb_fd_device *pdev =
+ container_of(dev, struct pcan_usb_fd_device, dev);
+
+ /* clear this channel's slot: dev is going to be freed */
+ WRITE_ONCE(pdev->usb_if->dev[dev->ctrl_idx], NULL);
+
/* last device: can free shared objects now */
if (!dev->prev_siblings && !dev->next_siblings) {
- struct pcan_usb_fd_device *pdev =
- container_of(dev, struct pcan_usb_fd_device, dev);
-
/* free commands buffer */
kfree(pdev->cmd_buffer_addr);
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v5 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (5 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-22 3:36 ` netdev-bot+sashiko
2026-10-20 13:30 ` [PATCH v5 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean via B4 Relay
` (3 subsequent siblings)
10 siblings, 1 reply; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel, stable
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
pcan_usb_encode_msg() appends a one-byte "writer id" whenever
PCAN_USB_TX_SRR is set in can_id_flags, for both RTR and non-RTR
frames: the data block and the SRR writer id byte are two independent,
unconditional steps.
pcan_usb_decode_data() only consumed that extra byte in the non-RTR
branch, inside the same conditional block that reads the CAN data
payload. A self-received RTR frame with the SRR bit set (e.g. a
loopback echo with CAN_CTRLMODE_LOOPBACK) therefore left the RX cursor
one byte short, causing the private id byte to be misparsed as the
next record's status/len byte and the rest of the URB to be misparsed
or rejected.
Move the private id byte skip out of the RTR/non-RTR branch so it is
applied whenever SRR is set, matching the encoder's layout.
Fixes: 3a7939495ce8 ("can: peak_usb: PCAN-USB: add support of loopback and one-shot mode")
Cc: stable@vger.kernel.org
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 93da0fd4f10e..4c8cfed31634 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -697,16 +697,19 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len)
memcpy(cf->data, mc->ptr, cf->len);
mc->ptr += rec_len;
- /* Ignore next byte (client private id) if SRR bit is set */
- if (can_id_flags & PCAN_USB_TX_SRR) {
- if ((mc->ptr + 1) > mc->end)
- goto decode_failed;
- mc->ptr++;
- }
-
/* update statistics */
mc->netdev->stats.rx_bytes += cf->len;
}
+
+ /* Ignore next byte (client private id) if SRR bit is set.
+ * Note: this test must be done even when the frame is an RTR frame.
+ */
+ if (can_id_flags & PCAN_USB_TX_SRR) {
+ if ((mc->ptr + 1) > mc->end)
+ goto decode_failed;
+ mc->ptr++;
+ }
+
mc->netdev->stats.rx_packets++;
/* convert timestamp into kernel time */
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v5 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
2026-10-20 13:30 ` [PATCH v5 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean via B4 Relay
@ 2026-10-22 3:36 ` netdev-bot+sashiko
0 siblings, 0 replies; 17+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 3:36 UTC (permalink / raw)
To: s.grosjean; +Cc: mkl, mailhol, error27, wg, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] In pcan_usb_decode_data()
(drivers/net/can/usb/peak_usb/pcan_usb.c), the patch moves the SRR…
--
can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
This patch moves the SRR writer-id byte skip in pcan_usb_decode_data()
out of the non-RTR branch. The byte is now consumed for RTR frames too,
which matches the layout produced by pcan_usb_encode_msg(). Before this,
self-received RTR frames with SRR set left the RX cursor one byte short.
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index 93da0fd4f10e7..4c8cfed31634a 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -697,16 +697,19 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len)
> memcpy(cf->data, mc->ptr, cf->len);
> mc->ptr += rec_len;
>
> - /* Ignore next byte (client private id) if SRR bit is set */
> - if (can_id_flags & PCAN_USB_TX_SRR) {
> - if ((mc->ptr + 1) > mc->end)
> - goto decode_failed;
> - mc->ptr++;
> - }
> -
> /* update statistics */
> mc->netdev->stats.rx_bytes += cf->len;
> }
> +
> + /* Ignore next byte (client private id) if SRR bit is set.
> + * Note: this test must be done even when the frame is an RTR frame.
> + */
> + if (can_id_flags & PCAN_USB_TX_SRR) {
> + if ((mc->ptr + 1) > mc->end)
> + goto decode_failed;
[Severity: Low]
Can rx_bytes now be over-counted for a frame that gets dropped?
The SRR bounds check now runs after the rx_bytes update in the non-RTR
branch. Take a non-RTR record with PCAN_USB_TX_SRR set in can_id_flags
where the URB ends right after the payload, so the writer-id byte is
missing:
pcan_usb_decode_data()
if ((mc->ptr + rec_len) > mc->end) /* passes */
...
mc->netdev->stats.rx_bytes += cf->len;
...
if ((mc->ptr + 1) > mc->end) /* fails */
goto decode_failed;
The skb is freed, -EINVAL is returned and rx_packets is not incremented,
but rx_bytes has already gone up by cf->len.
Before this patch the SRR check came before the rx_bytes update, so a
rejected record was not counted. Nothing undoes the increment later,
because peak_usb_read_bulk_callback() only calls pcan_dump_mem() when
dev_decode_buf() returns an error.
Would it work to move the rx_bytes update below the SRR check, for
example next to the rx_packets++?
> + mc->ptr++;
> + }
> +
> mc->netdev->stats.rx_packets++;
>
> /* convert timestamp into kernel time */
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v5-0-a62b35b867f0%40peak-system.fr
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v5 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (6 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-22 3:36 ` netdev-bot+sashiko
2026-10-20 13:30 ` [PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up Stéphane Grosjean via B4 Relay
` (2 subsequent siblings)
10 siblings, 1 reply; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
Records received from a PCAN-USB Pro device carry a CAN channel number
used to index the CAN device table usb_if->dev[]. The decoders only
check this number against the size of the table.
The slot of channel 1 stays NULL until that channel has been
initialized, while the RX URBs of channel 0, which share the same
bulk-in endpoint, may already be running. A record for channel 1
received in that window leads to a NULL pointer dereference in URB
completion context. Moreover, when probing channel 1 fails after its
slot has been filled, the slot keeps pointing to the freed channel.
Reject records for a channel whose slot is empty, and clear the slot
of a channel in pcan_usb_pro_free(). Slots are published with
smp_store_release() and read with READ_ONCE().
Fixes: d8a199355f8f ("can: usb: PEAK-System Technik PCAN-USB Pro specific part")
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index cbc23953ce61..151c5ea2859b 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -543,7 +543,12 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if,
if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
return -EINVAL;
- dev = usb_if->dev[ctrl_idx];
+ dev = READ_ONCE(usb_if->dev[ctrl_idx]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
+ return -EINVAL;
+
netdev = dev->netdev;
skb = alloc_can_skb(netdev, &can_frame);
@@ -590,7 +595,12 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if,
if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
return -EINVAL;
- dev = usb_if->dev[ctrl_idx];
+ dev = READ_ONCE(usb_if->dev[ctrl_idx]);
+
+ /* reject records for a channel that is not (or no longer) set up */
+ if (!dev)
+ return -EINVAL;
+
netdev = dev->netdev;
/* nothing should be sent while in BUS_OFF state */
@@ -933,7 +943,8 @@ static int pcan_usb_pro_init(struct peak_usb_device *dev)
}
pdev->usb_if = usb_if;
- usb_if->dev[dev->ctrl_idx] = dev;
+ /* publish dev to the decoders */
+ smp_store_release(&usb_if->dev[dev->ctrl_idx], dev);
/* set LED in default state (end of init phase) */
pcan_usb_pro_set_led(dev, PCAN_USBPRO_LED_DEVICE, 1);
@@ -981,6 +992,9 @@ static void pcan_usb_pro_exit(struct peak_usb_device *dev)
*/
static void pcan_usb_pro_free(struct peak_usb_device *dev)
{
+ /* clear this channel's slot: dev is going to be freed */
+ WRITE_ONCE(pcan_usb_pro_dev_if(dev)->dev[dev->ctrl_idx], NULL);
+
/* last device: can free pcan_usb_pro_interface object now */
if (!dev->prev_siblings && !dev->next_siblings)
kfree(pcan_usb_pro_dev_if(dev));
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v5 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro
2026-10-20 13:30 ` [PATCH v5 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean via B4 Relay
@ 2026-10-22 3:36 ` netdev-bot+sashiko
0 siblings, 0 replies; 17+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 3:36 UTC (permalink / raw)
To: s.grosjean; +Cc: mkl, mailhol, error27, wg, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] In pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error()
(drivers/net/can/usb/peak_usb/pcan_usb_pro.c), a well-formed record…
Pre-existing issues:
- [High] At this commit, if channel 0 is opened by userspace while still
in peak_usb_create_dev() (register_candev() runs before dev_init() and…
- [High] pcan_usb_pro_handle_canmsg()
(drivers/net/can/usb/peak_usb/pcan_usb_pro.c:559-567) takes the…
- [Medium] pcan_usb_pro_decode_buf() reads pr->data_type for the next
record before checking that rec_ptr < msg_end.
--
can: peak_usb: reject records for unset channels in PCAN-USB Pro
This patch changes the PCAN-USB Pro record decoders so they reject CAN
and status records whose channel slot in usb_if->dev[] is NULL. It also
clears the channel slot in pcan_usb_pro_free(). Slots are published with
smp_store_release() and read with READ_ONCE().
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> index cbc23953ce610..151c5ea2859be 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> @@ -543,7 +543,12 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if,
> if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
> return -EINVAL;
>
> - dev = usb_if->dev[ctrl_idx];
> + dev = READ_ONCE(usb_if->dev[ctrl_idx]);
> +
> + /* reject records for a channel that is not (or no longer) set up */
> + if (!dev)
> + return -EINVAL;
[Severity: Low]
Should a well-formed record for an unset channel cause the rest of the
transfer to be dropped?
pcan_usb_pro_decode_buf() treats any negative return from the handlers
as fatal:
case PCAN_USBPRO_RXRTR:
err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg);
if (err < 0)
goto fail;
Suppose channel 0 is open while channel 1's dev_init() is still running.
A channel 1 record then makes the decoder skip every later record in the
same bulk-IN transfer, including channel 0 frames and RXTS records.
The buffer is then dumped twice: once in pcan_usb_pro_decode_buf() and
once in peak_usb_read_bulk_callback(). Neither dump is rate limited at
this commit.
Would it be better to skip just this record, for example by returning 0?
The same applies to the new check in pcan_usb_pro_handle_error().
Later in the series, "can: peak_usb: register the netdevs only once all
channels are set up" moves register_candev() into peak_usb_probe(),
after all channels are initialized. That seems to make this branch
unreachable by the end of the series. Also, "can: peak_usb: only dump
received bytes on decode error" rate-limits the dump in the core.
> +
> netdev = dev->netdev;
>
> skb = alloc_can_skb(netdev, &can_frame);
[Severity: High]
This is a pre-existing issue and was not introduced by this patch. Since
this function is being changed anyway: can the payload memcpy() further
down in pcan_usb_pro_handle_canmsg() read past the record and past the
RX buffer?
pcan_usb_pro_handle_canmsg() {
...
can_frame->len = rx->len & 0x0f;
...
memcpy(can_frame->data, rx->data, can_frame->len);
...
}
The length comes from a nibble set by the device (0..15). The only check
in pcan_usb_pro_decode_buf() is that the record size chosen by data_type
fits in the transfer:
[PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4,
[PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
An RXMSG0 record is 12 bytes, so its data[] lies entirely after the
record. An RXMSG0 or RXMSG4 record with a length nibble of 8 would copy
bytes from past the end of the record.
Those bytes are stale or uninitialized parts of the 1024-byte RX buffer
that peak_usb_start() allocates with kmalloc(). If the record ends at
exactly 1024 bytes, the bytes come from beyond the allocation.
The frame passes can_is_can_skb() because len <= 8, and it is delivered
through netif_rx(). Can those kernel bytes then reach CAN_RAW sockets
through raw_recvmsg()?
With a nibble of 9..15, the memcpy() writes past the 8-byte
can_frame->data field into skb tailroom. That would trip the
FORTIFY_SOURCE field-spanning write check.
The issue is still present at the end of the series. Could the code
reject len > CAN_MAX_DLEN? It could also reject any length larger than
the record format provides: 0 for RXMSG0 and RXRTR, 4 for RXMSG4, and 8
for RXMSG8.
[ ... ]
> @@ -590,7 +595,12 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if,
> if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
> return -EINVAL;
>
> - dev = usb_if->dev[ctrl_idx];
> + dev = READ_ONCE(usb_if->dev[ctrl_idx]);
> +
> + /* reject records for a channel that is not (or no longer) set up */
> + if (!dev)
> + return -EINVAL;
> +
> netdev = dev->netdev;
[Severity: Medium]
This isn't a bug introduced by this patch, but does the caller,
pcan_usb_pro_decode_buf(), read pr->data_type before checking rec_ptr
against msg_end?
for (; rec_cnt > 0; rec_cnt--) {
union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr;
u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type];
rec_cnt comes from the device. Take a 1024-byte transfer holding the
4-byte header and 51 complete RXMSG8 records (4 + 51 * 20 = 1024), with
rec_cnt set to 52. After the last record, rec_ptr points to the end of
the allocation, and the next iteration reads one byte past it.
The byte is only used as an index into the 256-entry table, and the
result then fails one of the checks that follow. The effect is a 1-byte
out-of-bounds slab read that KASAN would report. This code is unchanged
at the end of the series.
[ ... ]
> @@ -981,6 +992,9 @@ static void pcan_usb_pro_exit(struct peak_usb_device *dev)
> */
> static void pcan_usb_pro_free(struct peak_usb_device *dev)
> {
> + /* clear this channel's slot: dev is going to be freed */
> + WRITE_ONCE(pcan_usb_pro_dev_if(dev)->dev[dev->ctrl_idx], NULL);
> +
> /* last device: can free pcan_usb_pro_interface object now */
> if (!dev->prev_siblings && !dev->next_siblings)
> kfree(pcan_usb_pro_dev_if(dev));
[Severity: High]
This is a pre-existing issue and was not introduced by this patch, but
can usb_if still be used after this kfree() for channel 0?
At this commit, peak_usb_create_dev() calls register_candev() before
dev_init() and dev_set_bus(). Suppose userspace opens can0 in that
window, and dev_set_bus(dev, 0) then fails. The error path runs:
adap_dev_free:
peak_usb_stop_rx(dev->prev_siblings); /* NULL for channel 0 */
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev); /* kfree(usb_if) */
lbl_unregister_candev:
unregister_candev(netdev);
peak_usb_stop_rx(NULL) stops nothing, so channel 0's own RX URBs keep
running. pcan_usb_pro_decode_buf() keeps using the usb_if returned by
pcan_usb_pro_dev_if(dev), and clearing the slot does not cover that
access.
unregister_candev() then closes the netdev, and ndo_stop reaches
pcan_usb_pro_stop(), which runs this on the freed object:
pdev->usb_if->dev_opened_count--;
Later in the series, "can: peak_usb: register the netdevs only once all
channels are set up" removes register_candev() and the
lbl_unregister_candev label from peak_usb_create_dev(). After that, no
netdev can be open when adap_dev_free runs, which seems to fix this.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v5-0-a62b35b867f0%40peak-system.fr
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (7 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-22 3:36 ` netdev-bot+sashiko
2026-10-20 13:30 ` [PATCH v5 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean via B4 Relay
2026-10-20 13:53 ` [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Dan Carpenter
10 siblings, 1 reply; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
peak_usb_create_dev() registers the netdev of a channel before calling
dev_init(), dev_set_bus() and dev_get_can_channel_id(). Userspace can
therefore open or configure the netdev while the channel is still being
initialized, outside of rtnl_lock():
- peak_usb_ndo_open() may run before dev_init() has set up the adapter
private data, e.g. pdev->usb_if, which is then dereferenced while
still NULL by pcan_usb_pro_start() or pcan_usb_fd_start();
- the commands sent by the open or bittiming callbacks may race with
the ones sent by dev_init() on the same command buffer;
- dev_set_bus(dev, 0) may switch the controller off right after
peak_usb_ndo_open() has started it.
On multi-channel adapters, the same applies to the channels already
created while a sibling is being initialized, since they share the
interface resources: on PCAN-USB FD, the command buffer and the
firmware info used by the sibling init, and the ctrlmode copied from
the first channel.
Moreover, if dev_init() or dev_set_bus() fails, the adapter private
data is released before unregister_candev(), which closes the netdev
if it has been opened in the meantime, and then accesses the released
data (interface object and, on PCAN-USB FD, command buffer).
Register the netdevs in peak_usb_probe(), only once all the channels
of the interface have been initialized, and only unregister the
registered ones in peak_usb_disconnect(). Since no netdev can be opened
during probe anymore, there is no RX to stop when probing a channel
fails. Since the channel id is now read before the netdev is
registered, PCAN-USB reports a failure to read it with dev_err()
instead of netdev_err().
Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core")
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb.c | 7 +++-
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 61 ++++++++++++++++------------
drivers/net/can/usb/peak_usb/pcan_usb_core.h | 1 +
3 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 4c8cfed31634..c7246465dcc7 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -390,9 +390,12 @@ static int pcan_usb_get_can_channel_id(struct peak_usb_device *dev, u32 *can_ch_
int err;
err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args);
+ /* explicit use of dev_xxx() instead of netdev_xxx() here: this is
+ * also called during probe, before the netdev is registered.
+ */
if (err)
- netdev_err(dev->netdev, "getting can channel id failure: %d\n", err);
-
+ dev_err(dev->netdev->dev.parent,
+ "getting can channel id failure: %d\n", err);
else
*can_ch_id = args[0];
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 4c08656e7768..a12ee6ec71e6 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -998,15 +998,6 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
SET_NETDEV_DEV(netdev, &intf->dev);
netdev->dev_id = ctrl_idx;
- err = register_candev(netdev);
- if (err) {
- dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
- goto lbl_restore_intf_data;
- }
-
- if (dev->prev_siblings)
- (dev->prev_siblings)->next_siblings = dev;
-
/* keep hw revision into the netdevice */
tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice);
dev->device_rev = tmp16 >> 8;
@@ -1014,7 +1005,7 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
if (dev->adapter->dev_init) {
err = dev->adapter->dev_init(dev);
if (err)
- goto lbl_unregister_candev;
+ goto lbl_restore_intf_data;
}
/* set bus off */
@@ -1027,24 +1018,15 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
/* get CAN channel id early */
dev->adapter->dev_get_can_channel_id(dev, &dev->can_channel_id);
- netdev_info(netdev, "attached to %s channel %u (device 0x%08X)\n",
- peak_usb_adapter->name, ctrl_idx, dev->can_channel_id);
+ if (dev->prev_siblings)
+ (dev->prev_siblings)->next_siblings = dev;
return 0;
adap_dev_free:
- /* the whole interface is released when probing a channel fails:
- * stop RX on the channels already created, since their RX URBs may
- * carry records for this one.
- */
- peak_usb_stop_rx(dev->prev_siblings);
-
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
-lbl_unregister_candev:
- unregister_candev(netdev);
-
lbl_restore_intf_data:
usb_set_intfdata(intf, dev->prev_siblings);
kfree(dev->cmd_buf);
@@ -1073,7 +1055,8 @@ static void peak_usb_disconnect(struct usb_interface *intf)
dev_prev_siblings = dev->prev_siblings;
strscpy(name, netdev->name, IFNAMSIZ);
- unregister_candev(netdev);
+ if (test_bit(PCAN_USB_STATE_REGISTERED, &dev->state))
+ unregister_candev(netdev);
kfree(dev->cmd_buf);
dev->next_siblings = NULL;
@@ -1094,6 +1077,7 @@ static int peak_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
const struct peak_usb_adapter *peak_usb_adapter;
+ struct peak_usb_device *dev;
int i, err = -ENOMEM;
/* get corresponding PCAN-USB adapter */
@@ -1108,13 +1092,40 @@ static int peak_usb_probe(struct usb_interface *intf,
for (i = 0; i < peak_usb_adapter->ctrl_count; i++) {
err = peak_usb_create_dev(peak_usb_adapter, intf, i);
+ if (err)
+ goto lbl_disconnect;
+ }
+
+ /* register the netdevs only once all the channels are set up: the
+ * channels of an interface share resources (command buffer, firmware
+ * info, ctrlmode copied from the 1st channel...) that must not be
+ * used through a netdev while a sibling is being initialized.
+ */
+ for (dev = usb_get_intfdata(intf); dev->prev_siblings;
+ dev = dev->prev_siblings)
+ ;
+
+ for (; dev; dev = dev->next_siblings) {
+ err = register_candev(dev->netdev);
if (err) {
- /* deregister already created devices */
- peak_usb_disconnect(intf);
- break;
+ dev_err(&intf->dev, "couldn't register CAN device: %d\n",
+ err);
+ goto lbl_disconnect;
}
+ set_bit(PCAN_USB_STATE_REGISTERED, &dev->state);
+
+ netdev_info(dev->netdev,
+ "attached to %s channel %u (device 0x%08X)\n",
+ peak_usb_adapter->name, dev->ctrl_idx,
+ dev->can_channel_id);
}
+ return 0;
+
+lbl_disconnect:
+ /* unregister (if needed) and free already created devices */
+ peak_usb_disconnect(intf);
+
return err;
}
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index cb8777c3e08e..614854c998de 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -114,6 +114,7 @@ struct peak_tx_urb_context {
*/
#define PCAN_USB_STATE_CONNECTED 0
#define PCAN_USB_STATE_STARTED 1
+#define PCAN_USB_STATE_REGISTERED 2
/* PEAK-System USB device */
struct peak_usb_device {
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up
2026-10-20 13:30 ` [PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up Stéphane Grosjean via B4 Relay
@ 2026-10-22 3:36 ` netdev-bot+sashiko
0 siblings, 0 replies; 17+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 3:36 UTC (permalink / raw)
To: s.grosjean; +Cc: mkl, mailhol, error27, wg, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] The patch moves register_candev() from peak_usb_create_dev() to
the end of peak_usb_probe().
- [Low] peak_usb_disconnect() is now also the probe-failure cleanup for
netdevs that were never registered.
Pre-existing issues:
- [High] Channel teardown never drains asynchronous restart URBs and
never cancels the legacy PCAN-USB restart_timer before freeing the…
- [High] pcan_usb_pro_handle_canmsg() sets can_frame->len = rx->len &
0x0f, a device-controlled value from 0 to 15.
- [Medium] pcan_usb_get_can_channel_id() declares 'u8
args[PCAN_USB_CMD_ARGS_LEN];' with no initializer.
- [Medium] peak_usb_disconnect() calls peak_usb_stop_rx() first, which
clears PCAN_USB_STATE_CONNECTED on every channel, and only then calls…
- [Low] Channel 0's dev_init() (pcan_usb_fd_init()/pcan_usb_pro_init())
sends drv_loaded(1), sets the clock domain and sets the LEDs to their…
--
can: peak_usb: register the netdevs only once all channels are set up
This commit moves register_candev() from peak_usb_create_dev() into
peak_usb_probe(). The netdevs are now registered only after every channel
of the interface has been initialized. peak_usb_disconnect() unregisters
only the netdevs flagged with the new PCAN_USB_STATE_REGISTERED bit, and
PCAN-USB now logs a failed channel id read with dev_err().
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index 4c8cfed31634a..c7246465dcc7b 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -390,9 +390,12 @@ static int pcan_usb_get_can_channel_id(struct peak_usb_device *dev, u32 *can_ch_
> int err;
>
> err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args);
> + /* explicit use of dev_xxx() instead of netdev_xxx() here: this is
> + * also called during probe, before the netdev is registered.
> + */
> if (err)
> - netdev_err(dev->netdev, "getting can channel id failure: %d\n", err);
> -
> + dev_err(dev->netdev->dev.parent,
> + "getting can channel id failure: %d\n", err);
[Severity: Low]
The comment says this also runs during probe, before the netdev is
registered. The helpers this function calls still log through
netdev_err(), though. Was the conversion meant to stop here?
pcan_usb_get_can_channel_id()->pcan_usb_wait_rsp() still does:
if (err)
netdev_err(dev->netdev,
"waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err);
pcan_usb_send_cmd() does the same with "sending cmd ...". The same holds
for pcan_usb_fd_send_cmd() in pcan_usb_fd.c, and for
pcan_usb_pro_send_cmd(), pcan_usb_pro_wait_rsp() and
pcan_usb_pro_send_req() in pcan_usb_pro.c. All of them are reached from
dev_init(), dev_set_bus(dev, 0) and dev_get_can_channel_id() in
peak_usb_create_dev().
register_candev() now runs later, in peak_usb_probe(), so netdev->name
still holds the "can%d" template at this point. __netdev_printk() will
print these messages as "(unnamed net_device) (uninitialized)". On
PCAN-USB, a failed channel id read would now print an
"(unnamed net_device) (uninitialized): waiting rsp ..." line just before
the new dev_err() line.
Should those helpers be converted as well?
> else
> *can_ch_id = args[0];
[Severity: Medium]
This is a pre-existing issue, not introduced by this patch, but can
args[0] be read here without ever being initialized?
args[] has no initializer. When the device is no longer connected,
pcan_usb_wait_rsp() returns 0 without filling it:
/* usb device unregistered? */
if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
peak_usb_disconnect() clears PCAN_USB_STATE_CONNECTED through
peak_usb_stop_rx() before calling unregister_candev(), so the netdev is
still registered during that window. An ETHTOOL_GEEPROM request that lands
there would go through peak_usb_get_eeprom()->dev_get_can_channel_id().
It would copy the uninitialized stack byte to userspace and cache it in
dev->can_channel_id.
The probe-time call this patch keeps runs with CONNECTED set, so it is
not affected.
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> index 4c08656e77681..a12ee6ec71e68 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
[ ... ]
> @@ -1073,7 +1055,8 @@ static void peak_usb_disconnect(struct usb_interface *intf)
> dev_prev_siblings = dev->prev_siblings;
> strscpy(name, netdev->name, IFNAMSIZ);
>
> - unregister_candev(netdev);
> + if (test_bit(PCAN_USB_STATE_REGISTERED, &dev->state))
> + unregister_candev(netdev);
[Severity: Low]
peak_usb_disconnect() now also cleans up netdevs that were never
registered. The PCAN_USB_STATE_REGISTERED test only guards
unregister_candev(). The name is still copied and printed for every
sibling:
strscpy(name, netdev->name, IFNAMSIZ);
...
dev_info(&intf->dev, "%s removed\n", name);
alloc_candev() leaves the "can%d" template in netdev->name until
register_netdevice() expands it. Will each unregistered channel then log
a literal "can%d removed"? That would happen when peak_usb_create_dev()
fails, or when register_candev() fails partway through the loop in
peak_usb_probe().
[Severity: Medium]
This isn't a bug introduced by this patch, but does the bus-off command
sent on close ever reach the device on this path?
peak_usb_disconnect() starts with:
/* stop RX on all channels before unregistering and freeing them */
peak_usb_stop_rx(usb_get_intfdata(intf));
This clears PCAN_USB_STATE_CONNECTED on every channel. For an opened
channel, unregister_candev() then calls peak_usb_ndo_stop(), which sends
dev_stop() and dev_set_bus(dev, 0).
Once CONNECTED is clear, pcan_usb_send_cmd(), pcan_usb_wait_rsp(),
pcan_usb_fd_send_cmd(), pcan_usb_pro_send_cmd() and
pcan_usb_pro_send_req() all return 0 without sending anything. Even so,
ndo_stop still sets dev->can.state = CAN_STATE_STOPPED.
The adapter can stay attached here: on a sysfs unbind, or on the probe
error path when a later register_candev() fails after an earlier sibling
was registered and opened. Wouldn't its CAN controller then stay bus-on
with no driver bound?
>
> kfree(dev->cmd_buf);
> dev->next_siblings = NULL;
[ ... ]
> @@ -1108,13 +1092,40 @@ static int peak_usb_probe(struct usb_interface *intf,
>
> for (i = 0; i < peak_usb_adapter->ctrl_count; i++) {
> err = peak_usb_create_dev(peak_usb_adapter, intf, i);
> + if (err)
> + goto lbl_disconnect;
> + }
> +
[ ... ]
> + for (; dev; dev = dev->next_siblings) {
> + err = register_candev(dev->netdev);
> if (err) {
> - /* deregister already created devices */
> - peak_usb_disconnect(intf);
> - break;
> + dev_err(&intf->dev, "couldn't register CAN device: %d\n",
> + err);
> + goto lbl_disconnect;
> }
[Severity: Low]
This is a pre-existing issue, but the hardware state set by dev_init()
does not seem to be undone on this path.
On PCAN-USB FD and Pro, channel 0's dev_init() (pcan_usb_fd_init() /
pcan_usb_pro_init()) sends drv_loaded(1) and sets the clock domain and
the LEDs. peak_usb_disconnect() only calls dev_free(), which frees memory
and sends no command.
The matching drv_loaded(0) and LED shutdown are in dev_exit()
(pcan_usb_fd_exit() / pcan_usb_pro_exit()). dev_exit() is only called
from peak_usb_do_device_exit() at module unload.
Is it intended that a failed probe leaves the adapter in its "driver
loaded" firmware state? With this patch, a register_candev() failure,
including for channel 0 on a single-channel adapter, also reaches this
teardown after the firmware has been initialized. A normal sysfs unbind
leaves the adapter in the same state.
> + set_bit(PCAN_USB_STATE_REGISTERED, &dev->state);
[ ... ]
> + return 0;
> +
> +lbl_disconnect:
> + /* unregister (if needed) and free already created devices */
> + peak_usb_disconnect(intf);
> +
> return err;
> }
[Severity: High]
This isn't a bug introduced by this patch, but can this teardown lead to
a use-after-free? Neither this error path nor peak_usb_disconnect()
drains the asynchronous restart URBs or cancels the PCAN-USB
restart_timer before free_candev().
On PCAN-USB, the restart URB completion always arms the timer, whatever
the URB status:
static void pcan_usb_restart_pending(struct urb *urb)
{
struct pcan_usb *pdev = urb->context;
/* the PCAN-USB needs time to restart */
mod_timer(&pdev->restart_timer,
jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT));
...
}
Nothing in the driver calls timer_delete_sync() or timer_shutdown_sync()
on restart_timer. On unplug or unbind, the endpoint flush completes the
URB with -ESHUTDOWN, which arms the timer. peak_usb_disconnect() then
frees the netdev, and the timer_list lives in that netdev's private area.
Up to 10ms later, this would run on freed memory:
pcan_usb_restart()
peak_usb_restart_complete(dev)
netif_wake_queue(dev->netdev)
On Pro and FD, pcan_usb_pro_restart_async() and
pcan_usb_fd_restart_async() submit unanchored URBs with the channel
pointer as urb->context. On this probe error path the USB core has not
disabled the interface.
Suppose register_candev() fails for a later channel while an earlier,
registered and opened channel has a restart in flight. Could
pcan_usb_pro_restart_complete()->peak_usb_restart_complete(urb->context)
then run after free_candev()?
[Severity: High]
This is a pre-existing issue in pcan_usb_pro.c and is not related to the
code changed here. Can pcan_usb_pro_handle_canmsg() copy past the end of
the record and past can_frame->data[]?
can_frame->len = rx->len & 0x0f;
...
memcpy(can_frame->data, rx->data, can_frame->len);
The length comes from the device, ranges from 0 to 15, and is not checked
against CAN_MAX_DLEN or the payload size of the record type.
pcan_usb_pro_decode_buf() only checks the fixed record size:
if (rec_ptr + sizeof_rec > msg_end) {
RXMSG0 records (12 bytes, no payload) and RXMSG4 records (16 bytes) both
reach the same handler:
[PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4,
[PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
A broken or malicious adapter could send such a record with a large
length nibble. The memcpy would then read beyond the record:
- into stale bytes of the non-zeroed kmalloc() RX buffer allocated in
peak_usb_start(), or
- past the end of that buffer, when the record sits at its end.
With len > 8 it would also write up to 7 bytes past can_frame->data[8].
Frames with len <= 8 that carry this data go to CAN_RAW readers through
netif_rx().
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v5-0-a62b35b867f0%40peak-system.fr
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v5 10/10] can: peak_usb: only dump received bytes on decode error
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (8 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 09/10] can: peak_usb: register the netdevs only once all channels are set up Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:30 ` Stéphane Grosjean via B4 Relay
2026-10-22 3:36 ` netdev-bot+sashiko
2026-10-20 13:53 ` [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Dan Carpenter
10 siblings, 1 reply; 17+ messages in thread
From: Stéphane Grosjean via B4 Relay @ 2026-10-20 13:30 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Dan Carpenter, Wolfgang Grandegger
Cc: Stéphane Grosjean, linux-can, linux-kernel
From: Stéphane Grosjean <s.grosjean@peak-system.fr>
When the decoding of a received URB fails, peak_usb_read_bulk_callback()
dumps the whole RX buffer to the kernel log. The buffer is allocated with
kmalloc() and its size is the adapter RX buffer size (up to 2048 bytes),
so the bytes beyond urb->actual_length are uninitialized memory or
leftovers from previous transfers.
Dump only the urb->actual_length bytes received from the device. Since a
faulty or malicious device can make every transfer fail to decode, also
rate limit the dump.
Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core")
Signed-off-by: Stéphane Grosjean <s.grosjean@peak-system.fr>
---
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index a12ee6ec71e6..e146c8940420 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -265,10 +265,10 @@ static void peak_usb_read_bulk_callback(struct urb *urb)
/* handle these kinds of msgs only if _start callback called */
if (test_bit(PCAN_USB_STATE_STARTED, &dev->state)) {
err = dev->adapter->dev_decode_buf(dev, urb);
- if (err)
+ if (err && net_ratelimit())
pcan_dump_mem("received usb message",
urb->transfer_buffer,
- urb->transfer_buffer_length);
+ urb->actual_length);
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v5 10/10] can: peak_usb: only dump received bytes on decode error
2026-10-20 13:30 ` [PATCH v5 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean via B4 Relay
@ 2026-10-22 3:36 ` netdev-bot+sashiko
0 siblings, 0 replies; 17+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 3:36 UTC (permalink / raw)
To: s.grosjean; +Cc: mkl, mailhol, error27, wg, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 0 (1) · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [High] Incomplete fix: the patch does not meet its stated rate-limiting
goal for most of the PEAK adapters.
Pre-existing issues:
- [Medium] This problem wasn't introduced by this patch, but
pcan_usb_pro_decode_buf() reads pr->data_type at rec_ptr before…
--
can: peak_usb: only dump received bytes on decode error
When decoding a received URB fails, peak_usb_read_bulk_callback() now
dumps only urb->actual_length bytes instead of the whole RX buffer. The
dump is also rate limited, since a faulty or malicious device can make
every transfer fail to decode.
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> index a12ee6ec71e68..e146c8940420a 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> @@ -265,10 +265,10 @@ static void peak_usb_read_bulk_callback(struct urb *urb)
> /* handle these kinds of msgs only if _start callback called */
> if (test_bit(PCAN_USB_STATE_STARTED, &dev->state)) {
> err = dev->adapter->dev_decode_buf(dev, urb);
> - if (err)
> + if (err && net_ratelimit())
> pcan_dump_mem("received usb message",
> urb->transfer_buffer,
> - urb->transfer_buffer_length);
> + urb->actual_length);
[Severity: High]
Does this rate limit cover the FD and Pro adapters? The commit message
says:
Since a faulty or malicious device can make every transfer fail to
decode, also rate limit the dump.
For the PCAN-USB FD family and for PCAN-USB Pro, dev_decode_buf() has
already dumped the same bytes before it returns the error, and that dump
has no rate limit:
drivers/net/can/usb/peak_usb/pcan_usb_fd.c:pcan_usb_fd_decode_buf() {
...
fail:
if (err)
pcan_dump_mem("received msg",
urb->transfer_buffer, urb->actual_length);
return err;
}
pcan_usb_pro_decode_buf() in pcan_usb_pro.c has the same fail: block.
The device decides whether those paths run:
- In FD, a "got frag rec" or "got short rec" record returns -EBADMSG.
- In Pro, an unknown data_type returns -ENOTSUPP, and a record that
crosses msg_end returns -EBADMSG.
Each of those paths also prints a netdev_err() with no rate limit.
A device that fails every URB would therefore still flood the log with
one full hex dump per URB (up to 2048 bytes for FD), plus the
netdev_err(). On top of that, the core dump in this hunk prints the same
buffer a second time whenever net_ratelimit() allows it.
Only the original PCAN-USB adapter (pcan_usb.c) gets the intended
behaviour, because its decoder does not dump anything itself.
Should the adapter-level dumps and their netdev_err() calls also be rate
limited? Or could the core dump be skipped for adapters that already
dump? Either way, each decode error would be dumped at most once and
always under a rate limit.
[Severity: Medium]
This isn't a bug introduced by this patch, but I noticed it in the Pro
decode path. pcan_usb_pro_decode_buf() in pcan_usb_pro.c reads
pr->data_type before it checks rec_ptr against msg_end:
for (; rec_cnt > 0; rec_cnt--) {
union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr;
u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type];
...
/* check if the record goes out of current packet */
if (rec_ptr + sizeof_rec > msg_end) {
The device supplies rec_cnt. If a record ends exactly at msg_end and
rec_cnt is still non-zero, the next iteration reads *msg_end.
The RX buffer is kmalloc(PCAN_USBPRO_RX_BUFFER_SIZE), which is 1024
bytes. Take a 1024-byte transfer with a 4-byte header, rec_cnt = 86, and
85 RXTS records of 12 bytes each. The 86th iteration then reads
transfer_buffer[1024], one byte past the slab object. For shorter
transfers it reads stale bytes left in the buffer.
The byte only decides between -ENOTSUPP and -EBADMSG. Still, would KASAN
report this read as slab-out-of-bounds?
The FD decoder avoids this by checking msg_end - msg_ptr against
sizeof(*rx_msg) at the top of its loop. Could pcan_usb_pro_decode_buf()
use a similar rec_ptr >= msg_end check before the dereference?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v5-0-a62b35b867f0%40peak-system.fr
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening
2026-10-20 13:30 [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean via B4 Relay
` (9 preceding siblings ...)
2026-10-20 13:30 ` [PATCH v5 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean via B4 Relay
@ 2026-10-20 13:53 ` Dan Carpenter
2026-10-20 14:19 ` Stéphane Grosjean
10 siblings, 1 reply; 17+ messages in thread
From: Dan Carpenter @ 2026-10-20 13:53 UTC (permalink / raw)
To: s.grosjean
Cc: Marc Kleine-Budde, Vincent Mailhol, Wolfgang Grandegger,
linux-can, linux-kernel, Stefan Günther, stable
Could you please slow down. Only resend once per day. Otherwise it's
a bit like involuntary pair programming for people who are on the list.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening
2026-10-20 13:53 ` [PATCH v5 00/10] can: peak_usb: fixes, cleanup and input validation hardening Dan Carpenter
@ 2026-10-20 14:19 ` Stéphane Grosjean
0 siblings, 0 replies; 17+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 14:19 UTC (permalink / raw)
To: Dan Carpenter
Cc: Marc Kleine-Budde, Vincent Mailhol, Wolfgang Grandegger,
linux-can, linux-kernel, Stefan Günther, stable, s grosjean
Sorry about that, and thanks for the reminder. I'll wait at least
24 hours between postings from now on.
Regards,
Stéphane
----- Mail original -----
> Could you please slow down. Only resend once per day. Otherwise
> it's
> a bit like involuntary pair programming for people who are on the
> list.
>
> regards,
> dan carpenter
>
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread