Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH v2] can: restore skb header initialisations in init_can_skb()
@ 2026-09-17 12:37 ` zjamg
  2026-09-28  6:50   ` Quchaosheng
  0 siblings, 1 reply; 5+ messages in thread
From: zjamg @ 2026-09-17 12:37 UTC (permalink / raw)
  To: Marc Kleine-Budde, Vincent Mailhol, Oliver Hartkopp
  Cc: Paolo Abeni, linux-can, linux-kernel, zjamg, stable

Commit 9f10374bb024 ("can: remove private CAN skb headroom infrastructure")
removed the skb_reset_mac_header()/skb_reset_network_header()/
skb_reset_transport_header() calls from init_can_skb(). As a result, RX
skbs from alloc_can_skb() and friends again carry mac_header = 0xFFFF.
When such an skb reaches packet_rcv_spkt() (SOCK_PACKET), the push length
calculation overflows and triggers skb_under_panic -> kernel BUG -> full
machine panic.

The same issue was originally reported in 2014 on linux-can and fixed by
commit 969439016d2c ("can: add missing initialisations in CAN related
skbuffs"). packet_rcv_spkt() itself has never been hardened: only
packet_rcv() and tpacket_rcv() gained dev_has_header() checks in
commit d549699048b4 ("net/packet: fix packet receive on L3 devices
without visible hard header").

Fixes: 9f10374bb024 ("can: remove private CAN skb headroom infrastructure")
Cc: stable@vger.kernel.org
Reviewed-by: Oliver Hartkopp <socketcan@hartkopp.net>
Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: zjamg <ndaugoing@gmail.com>
---
v2:
 - Remove in-code comment per Oliver's feedback.
 - Pick up Reviewed-by and Acked-by tags from Oliver Hartkopp.

 drivers/net/can/dev/skb.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
index 95fcdc1026f8..14edec5afb57 100644
--- a/drivers/net/can/dev/skb.c
+++ b/drivers/net/can/dev/skb.c
@@ -210,6 +210,10 @@ static void init_can_skb(struct sk_buff *skb)
 {
 	skb->pkt_type = PACKET_BROADCAST;
 	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
 }
 
 struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2] can: isotp: check the frame type, not just the length
@ 2026-09-20  3:56 ` Kaixuan Li
  2026-09-20 18:13   ` Oliver Hartkopp
  2026-09-28  6:50   ` Quchaosheng
  0 siblings, 2 replies; 5+ messages in thread
From: Kaixuan Li @ 2026-09-20  3:56 UTC (permalink / raw)
  To: Oliver Hartkopp, Marc Kleine-Budde; +Cc: Kaixuan Li, linux-can, linux-kernel

isotp_rcv() separates Classic CAN from CAN FD by skb->len alone:

	if (skb->len != so->ll.mtu)
		return;

	cf = (struct canfd_frame *)skb->data;

A CAN XL frame with cxl->len 4 is CAN_MTU bytes, so it passes, and is then
read as a canfd_frame whose len comes out of canxl_frame.flags: at least
0x80.

Of the paths that follow, only the flow control one uses that length
without bounding it first, so check_pad() walks to 255 over a 16-byte
frame and the caller reports EBADMSG on an unrelated socket.

bcm_rx_handler(), j1939_can_recv(), can_can_gw_rcv() and raw_rcv() check
the frame type here, and can_dropped_invalid_skb() switches on
skb->protocol on the transmit side. isotp_rcv() is the gap.

Fixes: fb08cba12b52 ("can: canxl: update CAN infrastructure for CAN XL frames")
Signed-off-by: Kaixuan Li <kaixuanli0131@gmail.com>
Reviewed-by: Oliver Hartkopp <socketcan@hartkopp.net>
Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
v2: shorten the comment above the new check to say what it does; the
    reasoning stays in the description (Oliver Hartkopp). Add Oliver's
    Reviewed-by and Acked-by. No code change.

v1: https://lore.kernel.org/linux-can/20260919122852.1868961-1-kaixuanli0131@gmail.com/

Reproduced on v7.2.4 over vcan, one isotp socket per case bound rx 0x123
with RX_PADDING|CHK_PAD_DATA and rxpad_content 0xAA, a first frame in
flight, and one frame injected from a CAN_RAW socket.

	case                            stock     patched
	A CAN XL, cxl->len 4, flags ff  EBADMSG   none
	B Classic FC, padded 0xAA       none      none
	C Classic FC, padded 0x00       EBADMSG   EBADMSG
	D as A, with CHK_PAD_LEN on     EBADMSG   none

C bounds the impact: a malformed Classic FC frame from any sender on the
bus gives the same EBADMSG, so nothing becomes reachable that was not
already. D differs only in which branch of check_pad() returns.

No memory safety issue. KASAN was on for all eight runs and reported
nothing.
---
 net/can/isotp.c | 8 ++++++++
 1 file changed, 8 insertions(+)

--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -754,8 +754,16 @@ static void isotp_rcv(struct sk_buff *skb, void *data)
 	 */
 	if (skb->len != so->ll.mtu)
 		return;
 
+	/* check for correct CAN CC/FD frame content */
+	if (so->ll.mtu == CAN_MTU) {
+		if (!can_is_can_skb(skb))
+			return;
+	} else if (!can_is_canfd_skb(skb)) {
+		return;
+	}
+
 	cf = (struct canfd_frame *)skb->data;
 
 	/* if enabled: check reception of my configured extended address */
 	if (ae && cf->data[0] != so->opt.rx_ext_address)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] can: isotp: check the frame type, not just the length
  2026-09-20  3:56 ` [PATCH v2] can: isotp: check the frame type, not just the length Kaixuan Li
@ 2026-09-20 18:13   ` Oliver Hartkopp
  2026-09-28  6:50   ` Quchaosheng
  1 sibling, 0 replies; 5+ messages in thread
From: Oliver Hartkopp @ 2026-09-20 18:13 UTC (permalink / raw)
  To: Kaixuan Li, Marc Kleine-Budde; +Cc: linux-can, linux-kernel



On 20.09.26 05:56, Kaixuan Li wrote:
> isotp_rcv() separates Classic CAN from CAN FD by skb->len alone:
> 
> 	if (skb->len != so->ll.mtu)
> 		return;
> 
> 	cf = (struct canfd_frame *)skb->data;
> 
> A CAN XL frame with cxl->len 4 is CAN_MTU bytes, so it passes, and is then
> read as a canfd_frame whose len comes out of canxl_frame.flags: at least
> 0x80.
> 
> Of the paths that follow, only the flow control one uses that length
> without bounding it first, so check_pad() walks to 255 over a 16-byte
> frame and the caller reports EBADMSG on an unrelated socket.
> 
> bcm_rx_handler(), j1939_can_recv(), can_can_gw_rcv() and raw_rcv() check
> the frame type here, and can_dropped_invalid_skb() switches on
> skb->protocol on the transmit side. isotp_rcv() is the gap.
> 
> Fixes: fb08cba12b52 ("can: canxl: update CAN infrastructure for CAN XL frames")
> Signed-off-by: Kaixuan Li <kaixuanli0131@gmail.com>
> Reviewed-by: Oliver Hartkopp <socketcan@hartkopp.net>
> Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>
> ---
> v2: shorten the comment above the new check to say what it does; the
>      reasoning stays in the description (Oliver Hartkopp). Add Oliver's
>      Reviewed-by and Acked-by. No code change.
> 

Thanks for the fast update!

Awaiting upstream.

Best regards,
Oliver

> v1: https://lore.kernel.org/linux-can/20260919122852.1868961-1-kaixuanli0131@gmail.com/
> 
> Reproduced on v7.2.4 over vcan, one isotp socket per case bound rx 0x123
> with RX_PADDING|CHK_PAD_DATA and rxpad_content 0xAA, a first frame in
> flight, and one frame injected from a CAN_RAW socket.
> 
> 	case                            stock     patched
> 	A CAN XL, cxl->len 4, flags ff  EBADMSG   none
> 	B Classic FC, padded 0xAA       none      none
> 	C Classic FC, padded 0x00       EBADMSG   EBADMSG
> 	D as A, with CHK_PAD_LEN on     EBADMSG   none
> 
> C bounds the impact: a malformed Classic FC frame from any sender on the
> bus gives the same EBADMSG, so nothing becomes reachable that was not
> already. D differs only in which branch of check_pad() returns.
> 
> No memory safety issue. KASAN was on for all eight runs and reported
> nothing.
> ---
>   net/can/isotp.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
> 
> --- a/net/can/isotp.c
> +++ b/net/can/isotp.c
> @@ -754,8 +754,16 @@ static void isotp_rcv(struct sk_buff *skb, void *data)
>   	 */
>   	if (skb->len != so->ll.mtu)
>   		return;
>   
> +	/* check for correct CAN CC/FD frame content */
> +	if (so->ll.mtu == CAN_MTU) {
> +		if (!can_is_can_skb(skb))
> +			return;
> +	} else if (!can_is_canfd_skb(skb)) {
> +		return;
> +	}
> +
>   	cf = (struct canfd_frame *)skb->data;
>   
>   	/* if enabled: check reception of my configured extended address */
>   	if (ae && cf->data[0] != so->opt.rx_ext_address)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] can: isotp: check the frame type, not just the length
  2026-09-20  3:56 ` [PATCH v2] can: isotp: check the frame type, not just the length Kaixuan Li
  2026-09-20 18:13   ` Oliver Hartkopp
@ 2026-09-28  6:50   ` Quchaosheng
  1 sibling, 0 replies; 5+ messages in thread
From: Quchaosheng @ 2026-09-28  6:50 UTC (permalink / raw)
  To: Kaixuan Li
  Cc: Quchaosheng, Oliver Hartkopp, Marc Kleine-Budde, linux-can,
	linux-kernel

Hello Kaixuan,

I hit the same collision from a different direction and independently
arrived at the same fix, so:

Reviewed-by: Quchaosheng <quchaosheng000406@163.com>

Two things I checked that may be worth having on the record, since they
are the questions this patch is likely to attract.

First, that isotp really is the only gap, so it does not need to grow into
a series.  I went through the other places that consume a CAN skb without
looking at its type.  bcm_rx_handler() and can_can_gw_rcv() do gate on
can_is_can_skb() / can_is_canfd_skb(), and those two helpers carry a
second condition on the frame length field
(include/linux/can/skb.h:93 and :101).  On a CAN XL frame that field is
cxl->flags, and CANXL_XLF alone is 0x80, which is already past
CAN_MAX_DLEN and CANFD_MAX_DLEN, so an XL frame is rejected there before
its length is ever used.  So the collision is specific to isotp.

Second, nothing on the transmit side can produce it.  isotp always builds
its frames at so->ll.mtu and validates ll.mtu against CAN_MTU / CANFD_MTU
in the setsockopt path, so the frame that passes the old length-only test
can only come off the wire.

Your A/B table is what convinced me, by the way - case C in particular is
the right control, since it shows the EBADMSG already existed for a
malformed Classic FC from any sender.  Nothing becomes reachable that was
not reachable before.

Thanks for fixing this,
Quchaosheng


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] can: restore skb header initialisations in init_can_skb()
  2026-09-17 12:37 ` [PATCH v2] can: restore skb header initialisations in init_can_skb() zjamg
@ 2026-09-28  6:50   ` Quchaosheng
  0 siblings, 0 replies; 5+ messages in thread
From: Quchaosheng @ 2026-09-28  6:50 UTC (permalink / raw)
  To: zjamg
  Cc: Quchaosheng, Marc Kleine-Budde, Vincent Mailhol, Oliver Hartkopp,
	linux-can, linux-kernel, stable

Hello zjamg,

I reproduced the panic independently and verified that your patch alone
fixes it, so:

Tested-by: Quchaosheng <quchaosheng000406@163.com>

How I tested, in case it is useful for the maintainers.  The driver RX
path is required: vcan does not reproduce it, because can_send() resets
the headers itself on the way out.  I used slcan over a pty, opened a
SOCK_PACKET socket on the resulting can0, and pushed one frame in through
the line discipline.  Two kernels, same config, same initramfs, only your
patch differing.

Without the patch, v7.3.0-rc5 under QEMU:

  skbuff: skb_under_panic: text:ffffffffa0b28261 len:-65455 put:-65471 head:... data:... tail:0x50 end:0x180 dev:can0
  kernel BUG at net/core/skbuff.c:214!
  RIP: 0010:skb_panic+0x50/0x60
  Call Trace:
   <TASK>
   skb_push+0x4d/0x60
   packet_rcv_spkt+0xe1/0x170
  Kernel panic - not syncing: Fatal exception in interrupt

  (the len/put values match the ones in your report)

With your v2 applied verbatim, the identical run prints

  RESULT: survived, no skb_under_panic

and the guest powers off normally.

One note that may be worth adding to the commit message if you respin: the
2015 fix you reference, 969439016d2c, is the second time this class of bug
was closed on the producer side.  packet_rcv_spkt() has still never been
hardened, which is why the same failure mode came back a third time when
9f10374bb024 dropped the calls again.  I sent a separate patch for that
receiver-side guard so the next regression of this kind degrades to a
dropped frame instead of a panic - no overlap with this one, and yours is
the one that fixes the actual regression.

Thanks for picking this up,
Quchaosheng


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-28  6:51 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260928065053.1750765-1-quchaosheng000406@163.com>
2026-09-17 12:37 ` [PATCH v2] can: restore skb header initialisations in init_can_skb() zjamg
2026-09-28  6:50   ` Quchaosheng
2026-09-20  3:56 ` [PATCH v2] can: isotp: check the frame type, not just the length Kaixuan Li
2026-09-20 18:13   ` Oliver Hartkopp
2026-09-28  6:50   ` Quchaosheng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox