* [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening
@ 2026-10-20 10:40 Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean
` (9 more replies)
0 siblings, 10 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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
This series contains bug fixes, cleanups and input validation
improvements for the PEAK-System USB CAN drivers.
Patch 1 fixes CAN error frame generation, where CAN_ERR_CNT overwrote
previously set CAN error flags.
Patches 2, 3 and 5 are cleanups: make a public header self-contained,
sort the CAN_CTRLMODE_* flags in ctrlmode_supported, and replace
open-coded bitmask operations with bitops. Patch 5 also makes the
state flag updates atomic, which patch 6 relies on.
Patch 4 adds bounds checks to the PCAN-USB RX parser, so that
truncated or malformed records are rejected instead of being read
beyond the received data.
Patch 6 validates the channel numbers received from PCAN-USB FD
devices, rejects records for channels that are not set up, and stops
RX on all channels before they are released, on disconnect as well as
on probe failure.
Patch 7 fixes the RX parsing of self-received RTR frames with SRR set,
which left the RX cursor one byte behind.
Patches 8 to 10 fix related issues found while reviewing the above:
the same channel slot check in the PCAN-USB Pro driver, the netdev
being registered before its channel is initialized, and the dump of
stale RX buffer bytes on decode errors.
---
Changes in v4:
- Register the netdev only once the channel is initialized, instead of
only reordering unregister_candev() and dev_free() on probe failure
(patch 9).
- Link to v3: https://lore.kernel.org/r/20261020-canfd_check_channel_idx-v3-0-74d17c5a599e@peak-system.fr
Changes in v3:
- Fix out-of-bounds read of the error counters in PCAN-USB bus events.
- Harden channel handling in PCAN-USB FD: publish/read channel slots
with smp_store_release()/READ_ONCE(), clear them on teardown, stop RX
on all channels on disconnect and on the siblings on probe failure,
and reject ndo_open() once disconnected.
- Fix RX cursor desync for self-received RTR frames with SRR.
- Replace open-coded bitmask operations with bitops.
- Add channel slot checks to PCAN-USB Pro.
- Unregister the netdev before dev_free() on probe failure.
- Only dump received bytes, rate limited, on decode error.
- Link to v2: https://patch.msgid.link/20261009-canfd_check_channel_idx-v2-0-f7b772929791@peak-system.fr
Changes in v2:
- Kill all pending RX URBs before unregistering sibling netdevs.
- Reject records targeting unavailable channels during device
initialization in rare corner cases involving malformed device
input.
- Link to v1: https://patch.msgid.link/20261008-canfd_check_channel_idx-v1-0-0a3bb82f4e09@peak-system.fr
To: Marc Kleine-Budde <mkl@pengutronix.de>
To: Vincent Mailhol <mailhol@kernel.org>
To: Dan Carpenter <error27@gmail.com>
To: Wolfgang Grandegger <wg@grandegger.com>
Cc: Stéphane Grosjean <s.grosjean@peak-system.fr>
Cc: linux-can@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Marc Kleine-Budde (1):
can: peak_usb: add missing includes
Stefan Günther (1):
can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters
Stéphane Grosjean (8):
can: peak_usb: sort ctrlmode_supported flags
can: peak_usb: harden PCAN-USB message validation
can: peak_usb: replace open-coded bitmask operations with bitops
can: peak_usb: harden channel handling in PCAN-USB FD
can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
can: peak_usb: reject records for unset channels in PCAN-USB Pro
can: peak_usb: register the netdev only once the channel is set up
can: peak_usb: only dump received bytes on decode error
drivers/net/can/usb/peak_usb/pcan_usb.c | 51 ++++++++++-----
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 68 ++++++++++++++------
drivers/net/can/usb/peak_usb/pcan_usb_core.h | 15 ++++-
drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 96 ++++++++++++++++++++--------
drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 29 +++++++--
5 files changed, 183 insertions(+), 76 deletions(-)
---
base-commit: a7bfaba4823e3c165bb2004c74eff7c096672bc7
change-id: 20261007-canfd_check_channel_idx-a4d7a1a1ee7d
Best regards,
--
Stéphane Grosjean <s.grosjean@peak-system.fr>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 02/10] can: peak_usb: add missing includes Stéphane Grosjean
` (8 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 02/10] can: peak_usb: add missing includes
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 03/10] can: peak_usb: sort ctrlmode_supported flags Stéphane Grosjean
` (7 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 03/10] can: peak_usb: sort ctrlmode_supported flags
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 02/10] can: peak_usb: add missing includes Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 04/10] can: peak_usb: harden PCAN-USB message validation Stéphane Grosjean
` (6 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 04/10] can: peak_usb: harden PCAN-USB message validation
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (2 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 03/10] can: peak_usb: sort ctrlmode_supported flags Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 05/10] can: peak_usb: replace open-coded bitmask operations with bitops Stéphane Grosjean
` (5 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 05/10] can: peak_usb: replace open-coded bitmask operations with bitops
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (3 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 04/10] can: peak_usb: harden PCAN-USB message validation Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean
` (4 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (4 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 05/10] can: peak_usb: replace open-coded bitmask operations with bitops Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean
` (3 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (5 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean
` (2 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (6 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean
9 siblings, 1 reply; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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] 16+ messages in thread
* [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (7 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean
9 siblings, 1 reply; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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 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.
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 netdev only once the channel has been fully initialized.
The error paths then no longer need to unregister it. Since the channel id
may now be 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 | 26 +++++++++++++-------------
2 files changed, 18 insertions(+), 15 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..ccfc26d3e5a6 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,6 +1018,18 @@ 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);
+ /* register the netdev only once the channel is fully set up, so that
+ * it can't be opened or configured while being initialized.
+ */
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
+ goto adap_dev_free;
+ }
+
+ if (dev->prev_siblings)
+ (dev->prev_siblings)->next_siblings = dev;
+
netdev_info(netdev, "attached to %s channel %u (device 0x%08X)\n",
peak_usb_adapter->name, ctrl_idx, dev->can_channel_id);
@@ -1042,9 +1045,6 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter,
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);
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
` (8 preceding siblings ...)
2026-10-20 10:40 ` [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up Stéphane Grosjean
@ 2026-10-20 10:40 ` Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
9 siblings, 1 reply; 16+ messages in thread
From: Stéphane Grosjean @ 2026-10-20 10:40 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 ccfc26d3e5a6..b86ea6f6b07c 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] 16+ messages in thread
* Re: [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD
2026-10-20 10:40 ` [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean
@ 2026-10-22 0:29 ` netdev-bot+sashiko
0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 0:29 UTC (permalink / raw)
To: stephane.grosjean
Cc: mkl, mailhol, error27, wg, s.grosjean, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 1 (2) · Medium: 0 · Low: 0 (1)
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] At the target commit, peak_usb_create_dev() calls
register_candev() before dev_init() and dev_set_bus().
Pre-existing issues:
- [High] This is the same NULL-slot dereference this commit fixes for
PCAN-USB FD, but in the sibling PCAN-USB Pro driver, which the commit…
- [High] Asynchronous restart URBs are not tracked, so one can outlive a
channel freed on the probe-rollback path.
- [Low] On the probe-failure path the device is still attached.
--
can: peak_usb: harden channel handling in PCAN-USB FD
This patch makes the PCAN-USB FD decoders check the record's channel
number against the adapter's controller count and reject records for
empty channel slots. It also clears a channel's slot in pcan_usb_fd_free().
It adds peak_usb_stop_rx(), which stops RX on the siblings already created
when a channel probe fails, and on all channels in peak_usb_disconnect().
> 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 a1b17e1e6fea6..4c08656e77681 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
[ ... ]
> @@ -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);
[Severity: Low]
This is a pre-existing issue, but the adapter is still attached on the
probe failure path. Once CONNECTED is cleared here, pcan_usb_fd_send_cmd()
returns success without sending anything:
pcan_usb_fd_send_cmd() {
...
/* usb device unregistered? */
if (!test_bit(PCAN_USB_STATE_CONNECTED, &dev->state))
return 0;
...
}
Later, peak_usb_disconnect()->unregister_candev()->peak_usb_ndo_stop() sends
the calibration-off command (through pcan_usb_fd_stop()) and the bus-off
command (through dev_set_bus(dev, 0)). Are both of these dropped silently?
If so, a channel 0 that was already opened stays bus-on. Meanwhile the
driver sets dev->can.state to CAN_STATE_STOPPED and decrements
dev_opened_count.
Before this patch, peak_usb_disconnect() cleared CONNECTED just before
unregister_candev() for each channel, so these commands were already being
dropped the same way.
> +
> + for (d = dev; d; d = d->prev_siblings)
> + usb_poison_anchored_urbs(&d->rx_submitted);
> +}
> +
[ ... ]
> @@ -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);
>
[Severity: High]
At this commit, peak_usb_create_dev() still calls register_candev() before
dev_init() and dev_set_bus():
err = register_candev(netdev);
...
if (dev->adapter->dev_init) {
err = dev->adapter->dev_init(dev);
...
if (dev->adapter->dev_set_bus) {
err = dev->adapter->dev_set_bus(dev, 0);
if (err)
goto adap_dev_free;
}
If dev_set_bus() fails, the netdev of the failing channel may already be
open, with its own RX URBs in flight. peak_usb_stop_rx(dev->prev_siblings)
stops only the earlier siblings, not dev.
For channel 0, dev_free() is pcan_usb_fd_free(). It frees pdev->usb_if and
pdev->cmd_buffer_addr, because no siblings exist yet. Then:
lbl_unregister_candev:
unregister_candev(netdev);
peak_usb_ndo_stop()
peak_usb_unlink_all_urbs()
pcan_usb_fd_stop()
pdev->usb_if->dev_opened_count--;
Could this channel's RX completions, which are still running and decode
through pdev->usb_if, touch the freed usb_if? pcan_usb_fd_stop() above
would touch it too. Does this path need peak_usb_stop_rx(dev), so that the
failing channel itself is stopped as well?
The order of dev_free() before unregister_candev() predates this patch. A
later patch in this series, "can: peak_usb: register the netdev only once
the channel is set up", moves register_candev() after dev_init(),
dev_set_bus() and dev_get_can_channel_id(). It also drops
unregister_candev() from the error path, and its commit message describes
this use-after-free.
> @@ -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);
[Severity: High]
This isn't a bug introduced by this patch, but this path is now being
hardened for the probe error case. Can an asynchronous restart URB outlive
the peak_usb_device freed here?
pcan_usb_fd_restart_async() submits the restart URB with dev as its
context. It does not anchor the URB or take a reference on dev.
pcan_usb_pro_restart_async() works the same way:
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, fw_info->cmd_out_ep),
buf, pc - buf,
pcan_usb_pro_restart_complete, dev);
/* and submit it. */
return usb_submit_urb(urb, GFP_ATOMIC);
On a two-channel adapter, can0 can be up while channel 1 is being probed,
with a restart pending from restart-ms or from a manual restart. If the
channel 1 probe then fails:
peak_usb_probe()
peak_usb_disconnect()
peak_usb_stop_rx() poisons rx_submitted only
unregister_candev()
peak_usb_ndo_stop()
close_candev() cancels restart_work only
peak_usb_unlink_all_urbs() kills rx_submitted and tx_submitted
free_candev()
The USB core has not disabled the interface on this path. When the restart
URB completes later:
pcan_usb_pro_restart_complete()
peak_usb_restart_complete(urb->context)
dev->can.state = CAN_STATE_ERROR_ACTIVE;
netif_wake_queue(dev->netdev);
Would this write to the freed dev and its net_device?
[ ... ]
> 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 15d275d6a1277..eee5b5eaa247d 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
[ ... ]
> @@ -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;
>
[Severity: High]
This is a pre-existing issue, but the sibling PCAN-USB Pro driver has the
same NULL slot dereference, and this patch doesn't change it. In
pcan_usb_pro.c, pcan_usb_pro_handle_canmsg() only checks the table size:
if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
return -EINVAL;
dev = usb_if->dev[ctrl_idx];
netdev = dev->netdev;
pcan_usb_pro_handle_error() does the same thing. The channel 1 slot stays
NULL until pcan_usb_pro_init() runs for that channel:
usb_if->dev[dev->ctrl_idx] = dev;
By then, channel 0's RX URBs on the shared bulk-in endpoint may already be
running. pcan_usb_pro_free() never clears the slot either.
Can a channel 1 record that arrives in that window cause a NULL
dereference in URB completion context?
A later patch in this series, "can: peak_usb: reject records for unset
channels in PCAN-USB Pro", adds the READ_ONCE() and the NULL slot check to
both handlers. It also publishes the slot with smp_store_release() in
pcan_usb_pro_init() and clears it with WRITE_ONCE() in pcan_usb_pro_free().
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v4-0-bfbde81f8bab%40peak-system.fr
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
2026-10-20 10:40 ` [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean
@ 2026-10-22 0:29 ` netdev-bot+sashiko
0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 0:29 UTC (permalink / raw)
To: stephane.grosjean
Cc: mkl, mailhol, error27, wg, s.grosjean, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 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).
- [Medium] This patch adds an error-path stats regression in
pcan_usb_decode_data() (drivers/net/can/usb/peak_usb/pcan_usb.c).
--
can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR
This commit moves the SRR private id byte skip in pcan_usb_decode_data()
out of the non-RTR branch. The byte is now consumed whenever
PCAN_USB_TX_SRR is set, which matches the layout used by
pcan_usb_encode_msg() and stops the RX cursor desync for self-received
RTR frames.
> 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;
> + mc->ptr++;
> + }
> +
> mc->netdev->stats.rx_packets++;
[Severity: Medium]
Can this leave rx_bytes counting a frame that gets dropped?
Before this patch, the SRR bounds check ran inside the non-RTR branch,
ahead of the rx_bytes update. Now rx_bytes is updated first, and the
check comes after it.
Take a non-RTR record that has PCAN_USB_TX_SRR set and a non-zero
payload ending exactly at mc->end, so the private id byte is missing.
In pcan_usb_decode_data():
mc->ptr += rec_len;
...
mc->netdev->stats.rx_bytes += cf->len;
}
...
if (can_id_flags & PCAN_USB_TX_SRR) {
if ((mc->ptr + 1) > mc->end)
goto decode_failed;
decode_failed frees the skb and returns -EINVAL. No frame is delivered
and rx_packets is not incremented, but the rx_bytes increment of up to
8 bytes is kept.
The device firmware controls this data, so a faulty or malicious USB
device could trigger it through:
peak_usb_read_bulk_callback()->pcan_usb_decode_buf()->
pcan_usb_decode_msg()->pcan_usb_decode_data()
Would it work to do the non-RTR rx_bytes update after the shared SRR
check succeeds? For example, next to rx_packets++ and guarded by
!(status_len & PCAN_USB_STATUSLEN_RTR).
>
> /* convert timestamp into kernel time */
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v4-0-bfbde81f8bab%40peak-system.fr
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro
2026-10-20 10:40 ` [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean
@ 2026-10-22 0:29 ` netdev-bot+sashiko
0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 0:29 UTC (permalink / raw)
To: stephane.grosjean
Cc: mkl, mailhol, error27, wg, s.grosjean, 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] pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error() now
return -EINVAL when usb_if->dev[ctrl_idx] is NULL.
Pre-existing issues:
- [High] At this commit, peak_usb_create_dev() registers the netdev
before dev_init()/dev_set_bus().
- [High] At this commit, peak_usb_create_dev() calls register_candev()
before dev->adapter->dev_init().
- [High] pcan_usb_pro_handle_canmsg() takes the payload length from the
device (`can_frame->len = rx->len & 0x0f`, so 0..15) and runs…
- [Medium] pcan_usb_pro_decode_buf() loops on the device-supplied rec_cnt
and reads pr->data_type (`pcan_usb_pro_sizeof_rec[pr->data_type]`)…
--
can: peak_usb: reject records for unset channels in PCAN-USB Pro
With this patch, the PCAN-USB Pro decoders reject CAN message and status
records for a channel whose slot in usb_if->dev[] is NULL. The slot is
now published with smp_store_release() in pcan_usb_pro_init() and
cleared in pcan_usb_pro_free().
> 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]
Is -EINVAL the right return value here? pcan_usb_pro_decode_buf() treats
any negative return from the record handlers as fatal for the whole
transfer:
pcan_usb_pro_decode_buf() {
...
case PCAN_USBPRO_RXRTR:
err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg);
if (err < 0)
goto fail;
break;
...
fail:
if (err)
pcan_dump_mem("received msg",
urb->transfer_buffer, urb->actual_length);
}
Both channels share the PCAN_USBPRO_EP_MSGIN bulk-in endpoint, and their
records sit back to back in a single transfer. Suppose one channel 1
record arrives before pcan_usb_pro_init() has published dev[1]. Then
every later record in that transfer is dropped, including valid
channel 0 CAN frames, channel 0 RXSTATUS records, and the RXTS records
that feed usb_if->time_ref.
pcan_usb_pro_decode_buf() also hex dumps each such transfer without rate
limiting. At this commit, peak_usb_read_bulk_callback() dumps it a
second time. A later patch in the series, "can: peak_usb: only dump
received bytes on decode error", rate limits that second dump. The
dump in pcan_usb_pro_decode_buf() and the dropped records are still
there at the end of the series.
sizeof_rec has already been computed and bounds checked for this record.
Could the handler return 0, or the loop skip just this record? Only the
record for the unset channel would then be rejected, which is what the
commit message describes. The same question applies to the new check
in pcan_usb_pro_handle_error().
> +
> 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 now validates the channel from the device, should the
payload length be validated as well? Further down in
pcan_usb_pro_handle_canmsg():
can_frame->len = rx->len & 0x0f;
...
} else {
memcpy(can_frame->data, rx->data, can_frame->len);
The device sets can_frame->len to any value from 0 to 15. Nothing checks
it against CAN_MAX_DLEN or against the payload the record type actually
carries. pcan_usb_pro_decode_buf() checks record bounds only against
the fixed size for each type:
[PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
[PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8,
A device could end a transfer with a 12 byte RXMSG0 record carrying
len = 8 (or an RXRTR record with PCAN_USBPRO_RTR clear). That record
passes the rec_ptr + sizeof_rec > msg_end check. Can the memcpy() then
read 8 bytes past urb->actual_length?
If the record ends exactly at the end of the 1024 byte RX buffer
kmalloc()ed in peak_usb_start(), the read runs past the slab object.
With a shorter transfer, it reads stale bytes from the buffer, which is
not zeroed. In both cases those bytes reach CAN sockets through
netif_rx().
Lengths 9 to 15 also write up to 7 bytes past can_frame->data[].
Could the length be clamped to CAN_MAX_DLEN and checked against the
record size? This code is unchanged at the end of the series.
[Severity: Medium]
This is another pre-existing issue that this patch did not introduce.
pcan_usb_pro_decode_buf() reads the type byte of the next record before
it checks that the record is inside the transfer:
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];
...
if (rec_ptr + sizeof_rec > msg_end) {
rec_cnt comes from the device. Suppose it overstates the number of
records and the previous record ended exactly at msg_end. Then
pr->data_type reads transfer_buffer[actual_length]. When actual_length
equals PCAN_USBPRO_RX_BUFFER_SIZE, is that a 1 byte out-of-bounds read
of the RX buffer?
The value is only used as an index into pcan_usb_pro_sizeof_rec[], so
nothing is passed on. Would a rec_ptr >= msg_end check at the top of
the loop be enough? This is still present at the end of the series.
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue and was not introduced by this patch. Does
the new !dev check get reached at all while pdev->usb_if is still NULL?
At this commit, peak_usb_create_dev() calls register_candev() before
dev->adapter->dev_init():
err = register_candev(netdev);
...
if (dev->adapter->dev_init) {
err = dev->adapter->dev_init(dev);
So the channel can be opened before pcan_usb_pro_init() sets
pdev->usb_if. In that window, pcan_usb_pro_start() dereferences the NULL
pointer:
if (pdev->usb_if->dev_opened_count == 0) {
An RX completion that reaches pcan_usb_pro_decode_buf() in that window
also gets usb_if == NULL from pcan_usb_pro_dev_if(). The record handlers
then dereference usb_if->dev[] or usb_if->cm_ignore_count before the
new check runs.
This looks fixed later in the series by "can: peak_usb: register the
netdev only once the channel is set up". That patch moves
register_candev() after dev_init() and dev_set_bus().
[ ... ]
> @@ -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. Can
this kfree() still race with channel 0's own RX URBs and with its
ndo_stop? At this commit, peak_usb_create_dev() does the following when
dev_set_bus() fails:
adap_dev_free:
...
peak_usb_stop_rx(dev->prev_siblings);
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
lbl_unregister_candev:
unregister_candev(netdev);
For channel 0, dev->prev_siblings is NULL, so no RX URBs are stopped.
There is no sibling yet either, so pcan_usb_pro_free() frees the shared
pcan_usb_pro_interface.
If channel 0 was opened after pcan_usb_pro_init(), its RX completions
still reach pcan_usb_pro_decode_buf(). That function reads
usb_if->dev[], time_ref and cm_ignore_count from the freed object.
unregister_candev() then calls ndo_stop, and pcan_usb_pro_stop() writes
to the freed memory:
pdev->usb_if->dev_opened_count--;
This also looks fixed later in the series by "can: peak_usb: register
the netdev only once the channel is set up". In that patch,
register_candev() comes after dev_set_bus(), and the adap_dev_free path
no longer calls unregister_candev().
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v4-0-bfbde81f8bab%40peak-system.fr
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up
2026-10-20 10:40 ` [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up Stéphane Grosjean
@ 2026-10-22 0:29 ` netdev-bot+sashiko
0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 0:29 UTC (permalink / raw)
To: stephane.grosjean
Cc: mkl, mailhol, error27, wg, s.grosjean, linux-can, linux-kernel, kuba
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 0 · Low: 1
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] The fix does not close the command-buffer race on multi-channel
PCAN-USB FD family adapters (PCAN-USB Pro FD and PCAN-USB X6,…
- [Low] The patch moves register_candev() (pcan_usb_core.c:1024) after
dev_init(), dev_set_bus(dev, 0) and dev_get_can_channel_id().
--
can: peak_usb: register the netdev only once the channel is set up
This patch moves register_candev() in peak_usb_create_dev() after
dev_init(), dev_set_bus() and dev_get_can_channel_id(). The netdev then
can't be opened or configured while the channel is still being set up.
The error paths no longer unregister the netdev, and PCAN-USB now uses
dev_err() when reading the channel id fails.
> 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..ccfc26d3e5a6b 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
[ ... ]
> @@ -1027,6 +1018,18 @@ 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);
>
> + /* register the netdev only once the channel is fully set up, so that
> + * it can't be opened or configured while being initialized.
> + */
> + err = register_candev(netdev);
[Severity: High]
Does this fully close the command buffer race on the multi-channel
PCAN-USB FD adapters (PCAN-USB Pro FD and PCAN-USB X6)?
The commit message says the patch addresses this case:
the commands sent by the open or bittiming callbacks may race with
the ones sent by dev_init() on the same command buffer;
On those adapters, pcan_usb_fd_init() makes every channel after the
first share the previous sibling's command buffer:
drivers/net/can/usb/peak_usb/pcan_usb_fd.c:pcan_usb_fd_init() {
...
pdev->usb_if = ppdev->usb_if;
pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr;
...
}
So one buffer, and one fw_info, serves the whole interface. Nothing in
peak_usb appears to lock either of them.
peak_usb_probe() calls peak_usb_create_dev() for each channel in turn.
With this patch, can0 is registered at the end of channel 0's
peak_usb_create_dev(). Channel 1's peak_usb_create_dev() then runs
without RTNL:
peak_usb_create_dev(ch1)
dev_init() -> pcan_usb_fd_init()
pcan_usb_fd_set_clock_domain()
pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF)
dev_set_bus(dev, 0) -> pcan_usb_fd_set_bus()
dev_get_can_channel_id() -> pcan_usb_fd_get_can_channel_id()
pcan_usb_fd_read_fwinfo(dev, &usb_if->fw_info)
pcan_usb_pro_send_req() /* memsets the shared fw_info */
Meanwhile, userspace holding only RTNL can bring up can0:
peak_usb_ndo_open() -> pcan_usb_fd_start()
pcan_usb_fd_set_filter_std() / pcan_usb_fd_set_options()
Userspace can also set can0's bittiming through
pcan_usb_fd_set_bittiming_slow(). Both paths build commands in the same
buffer and send them with pcan_usb_fd_send_cmd(). That function writes
the EOC marker into the buffer and reads the shared fw_info->cmd_out_ep:
drivers/net/can/usb/peak_usb/pcan_usb_fd.c:pcan_usb_fd_send_cmd() {
...
memset(cmd_tail, 0xff, sizeof(u64));
...
err = usb_bulk_msg(dev->udev,
usb_sndbulkpipe(dev->udev,
fw_info->cmd_out_ep),
...
}
Could channel 1's RESET_MODE and EOC overwrite can0's 64 FILTER_STD
records, or the other way round? Could can0's bulk transfer use a
cmd_out_ep that pcan_usb_pro_send_req() has just zeroed?
The cross-channel form of this race was already there before this
patch. Moving register_candev() within a single channel can't prevent
it, though.
A fix probably needs to serialize command building and sending per
interface. Two options are a mutex in struct pcan_usb_fd_if, or holding
RTNL across the probe-time command sequence.
The last patch in the series, "can: peak_usb: only dump received bytes
on decode error", only touches peak_usb_read_bulk_callback(). So this
race seems to remain at the end of the series.
> + if (err) {
> + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err);
> + goto adap_dev_free;
> + }
[Severity: Low]
Can this error path now leave the firmware reporting that a driver is
loaded?
register_candev() now runs after dev_init() has already sent commands
to the hardware. For channel 0, pcan_usb_fd_init() sends
pcan_usb_fd_drv_loaded(dev, 1), programs the clock domain and sets the
CAN LEDs to PCAN_UFD_LED_DEF. In the same way, pcan_usb_pro_init() sends
pcan_usb_pro_drv_loaded(dev, 1) and turns the device LED on.
register_candev() can fail, for example with -ENOMEM from
register_netdev(), a netdevice notifier veto, or a can_get_termination()
error. In that case the adap_dev_free path only calls peak_usb_stop_rx()
and dev_free(). pcan_usb_fd_free() and pcan_usb_pro_free() only clear
usb_if->dev[] and free memory.
Only pcan_usb_fd_exit() and pcan_usb_pro_exit() send drv_loaded(0) and
switch the LEDs off. dev_exit() is only called from
peak_usb_do_device_exit() at module unload.
Before this patch, register_candev() ran before any command reached the
device, so a registration failure left the hardware untouched.
The existing dev_set_bus() failure path and peak_usb_disconnect()
already leave the device in this state. The effect is limited to the
firmware status and the LEDs.
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v4-0-bfbde81f8bab%40peak-system.fr
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error
2026-10-20 10:40 ` [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean
@ 2026-10-22 0:29 ` netdev-bot+sashiko
0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-22 0:29 UTC (permalink / raw)
To: stephane.grosjean
Cc: mkl, mailhol, error27, wg, s.grosjean, 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 · 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] The patch only partly achieves its second goal.
--
can: peak_usb: only dump received bytes on decode error
This commit limits the dump in peak_usb_read_bulk_callback() to the
urb->actual_length bytes the device actually sent, instead of the whole
kmalloc()'ed RX buffer. It also puts the dump behind net_ratelimit(),
because 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 ccfc26d3e5a6b..b86ea6f6b07c6 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 flood described in the commit message for
the FD and Pro adapters?
dev->adapter->dev_decode_buf() runs before this check. For the PCAN-USB
FD family (USB FD, Pro FD, X6, Chip) it is pcan_usb_fd_decode_buf(). For
PCAN-USB Pro it is pcan_usb_pro_decode_buf(). Both decoders already dump
the buffer on error, with 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 ends the same way.
The device fully controls the paths that set err here. In
pcan_usb_fd_decode_buf() they are a truncated header, an oversized
rx_msg->size ("got frag rec: should inc usb rx buf sze"), a short record
("got short rec") and sub-decoder failures.
In pcan_usb_pro_decode_buf() they are an unsupported data_type ("got
unsupported rec in usb msg:"), a record that runs past msg_end ("got
frag rec: should inc usb rx buf size") and handler errors.
Each of these paths also calls netdev_err() with no rate limit.
After each failure, peak_usb_read_bulk_callback() resubmits the bulk-IN
URB, so the cycle repeats:
peak_usb_read_bulk_callback()
dev->adapter->dev_decode_buf()
pcan_usb_fd_decode_buf() / pcan_usb_pro_decode_buf()
netdev_err(...)
pcan_dump_mem(...) <- no rate limit
if (err && net_ratelimit())
pcan_dump_mem(...) <- same buffer, dumped again
usb_submit_urb(...)
On these adapters, can a device that fails every transfer still flood
the kernel log with up to rx_buffer_size bytes of hex per URB?
Only the original PCAN-USB adapter seems fully covered, since its decoder
in pcan_usb.c does not dump.
Should the adapter-level dumps in pcan_usb_fd.c and pcan_usb_pro.c also
be rate limited or removed? The other option would be to keep a single
rate-limited dump here in the core.
> }
> }
>
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-canfd_check_channel_idx-v4-0-bfbde81f8bab%40peak-system.fr
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-10-22 0:29 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 10:40 [PATCH v4 00/10] can: peak_usb: fixes, cleanup and input validation hardening Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 01/10] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 02/10] can: peak_usb: add missing includes Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 03/10] can: peak_usb: sort ctrlmode_supported flags Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 04/10] can: peak_usb: harden PCAN-USB message validation Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 05/10] can: peak_usb: replace open-coded bitmask operations with bitops Stéphane Grosjean
2026-10-20 10:40 ` [PATCH v4 06/10] can: peak_usb: harden channel handling in PCAN-USB FD Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 07/10] can: peak_usb: fix RX cursor desync for self-received RTR frames with SRR Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 08/10] can: peak_usb: reject records for unset channels in PCAN-USB Pro Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 09/10] can: peak_usb: register the netdev only once the channel is set up Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
2026-10-20 10:40 ` [PATCH v4 10/10] can: peak_usb: only dump received bytes on decode error Stéphane Grosjean
2026-10-22 0:29 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®