From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mo4-p01-ob.smtp.rzone.de (mo4-p01-ob.smtp.rzone.de [85.215.255.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B8A0232D7C7 for ; Tue, 13 Oct 2026 07:42:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=85.215.255.52 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791877375; cv=pass; b=HGs/gTFWz3vb+N99zakgfcUQhFrw/+gBIqdWR+bgMXxHkS/zy6Xmp8UdU95shuxNFZbg0uVIBldFE6S2yspVJTCi9M2/Uc0Xz42CrgZqjJUFGKQ8Df8s4k5ZCPnughj/ZQA8c3mws5kFCj6rdUozdVKOSroY9lb20W2K73VwRHI= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791877375; c=relaxed/simple; bh=C0DyEYWTGz+xCLyDBj8NmIVL6aHWHsj3XiNvZ9lpZF8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qJG3H3G8r/e9r2tskswN5FsGZ8jOI2SDpedeBs36bVHdA0rki1fHem5yOvygmLVptSzwWRrDiB5e0rUJM+iKrDgjX4JttP1sFUv+VUsaraEvTxVK5pKpKD+PHk/xPuBen+fh3HWMit2vPi+T+vSVzN5NPtBPHuOBqZhjjcjgcEE= ARC-Authentication-Results:i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=hartkopp.net; spf=fail smtp.mailfrom=hartkopp.net; dkim=pass (2048-bit key) header.d=hartkopp.net header.i=@hartkopp.net header.b=iTgTIIPo; dkim=permerror (0-bit key) header.d=hartkopp.net header.i=@hartkopp.net header.b=/F+jjXgx; arc=pass smtp.client-ip=85.215.255.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=hartkopp.net Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=hartkopp.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=hartkopp.net header.i=@hartkopp.net header.b="iTgTIIPo"; dkim=permerror (0-bit key) header.d=hartkopp.net header.i=@hartkopp.net header.b="/F+jjXgx" ARC-Seal: i=1; a=rsa-sha256; t=1791877370; cv=none; d=strato.com; s=strato-dkim-0002; b=todTKN/cofMHYeusJw+aAGuJbJ0ZXPi4qJ/r5PDvU2+TJmtUh0WCESpsLCO9T/ptHa bk6SIB8vdS5AGA39BnCcLROxhkxRFZuGgK05hKEPe7Th0ynymLOfke/n+leKtPNix27X ddwNi9d9Z7wZ3jqFAaiFjy+IXwc84vJo7m7jQz6FT22NrZjVd5zbOny8mGhc3lIvXN8f n3f5RozYtFc+AZWQhSQcG3yK+DDC+OZFgJ6XjtZPYE/plS5jrAqV0FTDt/h6520/MnXC VEu9c99k/6RdkJRw4fNTulKdqhxbkIEGzcC4Iro+jqlxPcnAm7eVGBy/ajZnYin7vZ3d GlFw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1791877370; s=strato-dkim-0002; d=strato.com; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=eN8quXKWNNYq+6io1+FEV0c8l+rKx/m4OAbQLjKpxok=; b=qx7WUaa8jiA0PCZvjQagJIyf3JzhPgWP0JIxb8Wh+c8xzS/M0Q/0yQiSxLm1C1QM7C wc0ZURlj8BPBt2RsMKspyS0U4eHW5aoKWr3L987OFY0bB3Y+f2pLMUeY3YUl1lDiZhD1 yzxFGazi8sCwghZDauPx/6s70wsDNfh7iHeTQ0Cvy9CH+/sOXmo6WcUYI+9uzhNUtfSk rWQ7aIHhAcgK28OBV3tbXGWhGGiJcT5LzyO7bDizf/ezNUVpUme8+a2bs9EvMgeAviSF 0pjaeQBLwWV9qdlXhKk85N+VS+zDURGZTXKGqss14YEEW8/vlFUEM3b3k9LRajl8ZyRy gCCw== ARC-Authentication-Results: i=1; strato.com; arc=none; dkim=none X-RZG-CLASS-ID: mo01 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1791877370; s=strato-dkim-0002; d=hartkopp.net; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=eN8quXKWNNYq+6io1+FEV0c8l+rKx/m4OAbQLjKpxok=; b=iTgTIIPosbuDg9BnnYiaoB5796JJpFn3nQyaBKmjk9IRPU+7S052mv/iTXLfNrzDFQ q5wsiICI+0zblncnxkY8ECf3T8BGZIt8Oc5Nkz4YQLNinkGfbkw9r4oKghtq2w6TvbYJ PnSJL4VgZJmtmUxagO/5xAZXVy30ROxxuqJ9zWMZ5wmqunohdbl3n9azyKPNaSCpIv9E WICxF06SEAneG7PhcgbNi2HbdnEtt9uuIlIt5yEKGUKE9VJriW65c9aVWhzHjYQx+Yc1 uTE/zEMlsLjGm7Ht2eN75MBEsuLcw3e3O0SUdaN+YNciEEJKK+/CaFmQ8nQdZj3Bm5aq mXCQ== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1791877370; s=strato-dkim-0003; d=hartkopp.net; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Cc:Date: From:Subject:Sender; bh=eN8quXKWNNYq+6io1+FEV0c8l+rKx/m4OAbQLjKpxok=; b=/F+jjXgxgOqoNqqU3Eik5DJsz0kqmkAki9ULE8v9bdVsqh9UtUa/DDYGy90oJqqBXw BgmZJ7lshJlxoLt2f6DA== X-RZG-AUTH: ":P2MHfkW8eP4Mre39l357AZT/I7AY/7nT2yrDxb8mjH4JKvMdQv2tTUsMrZpkO3Mw3lZ/t54cFxeEQ7s8bDup0Q==" Received: from vivo.lan by smtp.strato.de (RZmta 55.6.3 AUTH) with ESMTPSA id Kfdf2a29D7go4DU (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Tue, 13 Oct 2026 09:42:50 +0200 (CEST) From: Oliver Hartkopp To: stable@vger.kernel.org Cc: Marc Kleine-Budde , Oliver Hartkopp , Joerg Willmann Subject: [PATCH 6.6.y] can: fix unique skb identifier regression under RPS Date: Tue, 13 Oct 2026 09:42:43 +0200 Message-ID: <20261013074243.288346-1-socketcan@hartkopp.net> X-Mailer: git-send-email 2.53.0 In-Reply-To: <'2026101251-lying-swept-678d@gregkh'> References: <'2026101251-lying-swept-678d@gregkh'> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="us-ascii" commit 7093c3b8314a4ef7405c4066a2521398c455efe7 upstream. Commit d4fb6514ff8e ("can: use skb hash instead of private variable in headroom") moved the per-skb unique identifier used for raw_rcv() duplicate detection into skb->hash. With RPS enabled, get_rps_cpu() calls skb_get_hash() before the frame reaches the CAN subsystem. Since CAN skbs have no L3/L4 headers, the flow dissector assigns every CAN frame the same non-zero software hash. can_set_skb_uid() only generated a new identifier when skb->hash was 0, so it kept this constant hash. When the SLAB allocator later reused the same skb address, raw_rcv() mistook the next legitimate frame for a duplicate and dropped it. Fix this by storing the CAN UID in the CAN skb extension (struct can_skb_ext::can_skb_uid) instead of skb->hash, decoupling it from any hash the network stack may compute. can_set_skb_uid() keeps its "assign only if unset" behaviour, which preserves UIDs set before transmission (e.g. by isotp to identify echo frames) across the local loopback path. Frames whose extension is shared with another clone (e.g. via tc mirred or netem duplicate) are given a private extension copy before the UID is assigned, so that independently received skb clones from real CAN interfaces (skbcnt = 0) don't end up with the same UID. The limitation that tc mirred/netem skb duplicates might be dropped in raw_rcv for looped back isotp echo frame skbs is accepted. There is no valid use-case for tc mirred or netem together with isotp which is not capable to operate on different CAN interfaces simultaneously. For routing and modifying CAN frames together with isotp use can-gw. can-gw and vxcan additionally clear the UID of forwarded/duplicated frames so each newly routed frame gets its own unique identifier. [ backport to 6.6: no CAN skb extensions available, so the UID is kept in struct can_skb_priv::skbcnt in the headroom (effectively reverting d4fb6514ff8e) and isotp is adapted accordingly. As skb clones share the headroom, the private extension copy for tc mirred/netem duplicates is not applicable. The UID resets in can-gw and vxcan are omitted, as writing to the shared headroom of cloned skbs would also alter the UID of the original skb. This keeps the pre-d4fb6514ff8e behaviour for routed and vxcan forwarded frames. ] Fixes: d4fb6514ff8e ("can: use skb hash instead of private variable in headroom") Reported-by: Joerg Willmann Closes: https://lore.kernel.org/linux-can/2859AD3D-C805-41A0-9036-C5E8EE152419@clnt.de/ Signed-off-by: Oliver Hartkopp --- drivers/net/can/dev/skb.c | 2 ++ include/linux/can/skb.h | 2 ++ net/can/af_can.c | 6 ++---- net/can/bcm.c | 2 ++ net/can/isotp.c | 11 +++++++---- net/can/j1939/socket.c | 1 + net/can/j1939/transport.c | 2 ++ net/can/raw.c | 7 ++++--- 8 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 0da615afa04d..3ebd4f779b9b 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -200,10 +200,11 @@ static void init_can_skb_reserve(struct sk_buff *skb) skb_reset_mac_header(skb); skb_reset_network_header(skb); skb_reset_transport_header(skb); can_skb_reserve(skb); + can_skb_prv(skb)->skbcnt = 0; } struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) { struct sk_buff *skb; @@ -309,10 +310,11 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb) /* af_packet does not apply CAN skb specific settings */ if (skb->ip_summed == CHECKSUM_NONE) { /* init headroom */ can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; skb->ip_summed = CHECKSUM_UNNECESSARY; /* perform proper loopback on capable devices */ if (dev->flags & IFF_ECHO) diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 869ea574a40a..27d0ca115c6c 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -47,15 +47,17 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb); */ /** * struct can_skb_priv - private additional data inside CAN sk_buffs * @ifindex: ifindex of the first interface the CAN frame appeared on + * @skbcnt: atomic counter to have an unique id together with skb pointer * @frame_len: length of CAN frame in data link layer * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv { int ifindex; + u32 skbcnt; unsigned int frame_len; struct can_frame cf[]; }; static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb) diff --git a/net/can/af_can.c b/net/can/af_can.c index 54877f06cd78..69f794e82ca6 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -640,14 +640,12 @@ static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_buf } void can_set_skb_uid(struct sk_buff *skb) { /* create non-zero unique skb identifier together with *skb */ - while (!(skb->hash)) - skb->hash = atomic_inc_return(&skbcounter); - - skb->sw_hash = 1; + while (!(can_skb_prv(skb)->skbcnt)) + can_skb_prv(skb)->skbcnt = atomic_inc_return(&skbcounter); } EXPORT_SYMBOL(can_set_skb_uid); static void can_receive(struct sk_buff *skb, struct net_device *dev) { diff --git a/net/can/bcm.c b/net/can/bcm.c index 491495803167..fcd923c223f4 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -336,10 +336,11 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) if (!skb) goto out; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; skb_put_data(skb, cf, op->cfsiz); /* send with loopback */ skb->dev = dev; @@ -1579,10 +1580,11 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, kfree_skb(skb); return -ENODEV; } can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; skb->dev = dev; can_skb_set_owner(skb, sk); err = can_send(skb, 1); /* send with loopback */ dev_put(dev); diff --git a/net/can/isotp.c b/net/can/isotp.c index fded419bc34f..e448cfdb1f07 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -298,10 +298,11 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) return 1; } can_skb_reserve(nskb); can_skb_prv(nskb)->ifindex = dev->ifindex; + can_skb_prv(nskb)->skbcnt = 0; nskb->dev = dev; can_skb_set_owner(nskb, sk); ncf = (struct canfd_frame *)nskb->data; skb_put_zero(nskb, so->ll.mtu); @@ -869,10 +870,11 @@ static void isotp_send_cframe(struct isotp_sock *so) return; } can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; /* set uid in tx skb to identify CF echo frames */ can_set_skb_uid(skb); cf = (struct canfd_frame *)skb->data; @@ -897,11 +899,11 @@ static void isotp_send_cframe(struct isotp_sock *so) old_cfecho = READ_ONCE(so->cfecho); if (old_cfecho) pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho); /* set consecutive frame echo tag */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, can_skb_prv(skb)->skbcnt); /* send frame with local echo enabled */ can_send_ret = can_send(skb, 1); if (can_send_ret) { pr_notice_once("can-isotp: %s: can_send_ret %pe\n", @@ -960,11 +962,11 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) * (no isotp_rcv() caller here), so take it ourselves */ spin_lock(&so->rx_lock); /* so->cfecho may since belong to a new transfer; recheck under lock */ - if (READ_ONCE(so->cfecho) != skb->hash) + if (READ_ONCE(so->cfecho) != can_skb_prv(skb)->skbcnt) goto out_unlock; /* cancel local echo timeout */ hrtimer_cancel(&so->echotimer); @@ -1192,10 +1194,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_out_drop; } can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; /* set uid in tx skb to identify CF echo frames */ can_set_skb_uid(skb); so->tx.len = size; @@ -1233,11 +1236,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[SF_PCI_SZ4 + ae] = size; else cf->data[ae] |= size; /* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, can_skb_prv(skb)->skbcnt); } else { /* send first frame */ isotp_create_fframe(cf, so, ae); @@ -1250,11 +1253,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) /* disable wait for FCs due to activated block size */ so->txfc.bs = 0; /* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, can_skb_prv(skb)->skbcnt); } else { /* standard flow control check */ new_state = ISOTP_WAIT_FIRST_FC; /* start timeout for FC */ diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 4841aae2fcaa..e1d6d57c921b 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -893,10 +893,11 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, if (!skb) goto failure; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = ndev->ifindex; + can_skb_prv(skb)->skbcnt = 0; skb_reserve(skb, offsetof(struct can_frame, data)); ret = memcpy_from_msg(skb_put(skb, size), msg, size); if (ret < 0) goto free_skb; diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index a262a58588c2..61f0c6b68138 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -611,10 +611,11 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, return ERR_PTR(-ENOMEM); skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; + can_skb_prv(skb)->skbcnt = 0; /* reserve CAN header */ skb_reserve(skb, offsetof(struct can_frame, data)); /* skb->cb must be large enough to hold a j1939_sk_buff_cb structure */ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*re_skcb)); @@ -1550,10 +1551,11 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, return NULL; skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; + can_skb_prv(skb)->skbcnt = 0; skcb = j1939_skb_to_cb(skb); memcpy(skcb, rel_skcb, sizeof(*skcb)); session = j1939_session_new(priv, skb, size); if (!session) { diff --git a/net/can/raw.c b/net/can/raw.c index ad4b16476743..517f075a0aec 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -73,12 +73,12 @@ MODULE_ALIAS("can-proto-1"); * list containing only one item. This common case is optimized by * storing the single filter in dfilter, to avoid using dynamic memory. */ struct uniqframe { + u32 skbcnt; const struct sk_buff *skb; - u32 hash; unsigned int join_rx_count; }; struct raw_sock { struct sock sk; @@ -138,21 +138,21 @@ static void raw_rcv(struct sk_buff *oskb, void *data) (!ro->xl_frames && can_is_canxl_skb(oskb))) return; /* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr(ro->uniq)->skb == oskb && - this_cpu_ptr(ro->uniq)->hash == oskb->hash) { + this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) { if (!ro->join_filters) return; this_cpu_inc(ro->uniq->join_rx_count); /* drop frame until all enabled filters matched */ if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count) return; } else { this_cpu_ptr(ro->uniq)->skb = oskb; - this_cpu_ptr(ro->uniq)->hash = oskb->hash; + this_cpu_ptr(ro->uniq)->skbcnt = can_skb_prv(oskb)->skbcnt; this_cpu_ptr(ro->uniq)->join_rx_count = 1; /* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count > 1) return; } @@ -869,10 +869,11 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (!skb) goto put_dev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; /* fill the skb before testing for valid CAN frames */ err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err < 0) goto free_skb; -- 2.53.0