[PATCH net-next v7 2/6] can: add CAN skb extension infrastructure

Oliver Hartkopp via B4 Relay posted 6 patches 1 week, 2 days ago
There is a newer version of this series
[PATCH net-next v7 2/6] can: add CAN skb extension infrastructure
Posted by Oliver Hartkopp via B4 Relay 1 week, 2 days ago
From: Oliver Hartkopp <socketcan@hartkopp.net>

To remove the private CAN bus skb headroom infrastructure 8 bytes need to
be stored in the skb. The skb extensions are a common pattern and an easy
and efficient way to hold private data travelling along with the skb. We
only need the skb_ext_add() and skb_ext_find() functions to allocate and
access CAN specific content as the skb helpers to copy/clone/free skbs
automatically take care of skb extensions and their final removal.

This patch introduces the complete CAN skb extensions infrastructure:
- add struct can_skb_ext in new file include/net/can.h
- add include/net/can.h in MAINTAINERS
- add SKB_EXT_CAN to skbuff.c and skbuff.h
- select SKB_EXTENSIONS in Kconfig when CONFIG_CAN is enabled
- check for existing CAN skb extensions in can_rcv() in af_can.c
- add CAN skb extensions allocation at every skb_alloc() location
- duplicate the skb extensions if cloning outgoing skbs (framelen/gw_hops)
- introduce can_skb_ext_add() and can_skb_ext_find() helpers

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
 MAINTAINERS               |  1 +
 drivers/net/can/dev/skb.c | 56 +++++++++++++++++++++++++++++++++++++----------
 drivers/net/can/vxcan.c   | 14 ++++++++++++
 include/linux/can/skb.h   | 17 ++++++++++++++
 include/linux/skbuff.h    |  3 +++
 include/net/can.h         | 28 ++++++++++++++++++++++++
 net/can/Kconfig           |  1 +
 net/can/af_can.c          |  9 +++++---
 net/can/bcm.c             | 15 +++++++++++++
 net/can/gw.c              | 17 ++++++++++++++
 net/can/isotp.c           | 24 ++++++++++++++++++++
 net/can/j1939/socket.c    |  8 +++++++
 net/can/j1939/transport.c | 28 +++++++++++++++++++++++-
 net/can/raw.c             |  8 +++++++
 net/core/skbuff.c         |  4 ++++
 15 files changed, 218 insertions(+), 15 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index c3df85fd5acd..97f276375219 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5632,10 +5632,11 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
 F:	Documentation/networking/can.rst
 F:	Documentation/networking/iso15765-2.rst
 F:	include/linux/can/can-ml.h
 F:	include/linux/can/core.h
 F:	include/linux/can/skb.h
+F:	include/net/can.h
 F:	include/net/netns/can.h
 F:	include/uapi/linux/can.h
 F:	include/uapi/linux/can/bcm.h
 F:	include/uapi/linux/can/gw.h
 F:	include/uapi/linux/can/isotp.h
diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
index 0da615afa04d..c572745565f6 100644
--- a/drivers/net/can/dev/skb.c
+++ b/drivers/net/can/dev/skb.c
@@ -4,10 +4,11 @@
  * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
  */
 
 #include <linux/can/dev.h>
 #include <linux/module.h>
+#include <net/can.h>
 
 #define MOD_DESC "CAN device driver interface"
 
 MODULE_DESCRIPTION(MOD_DESC);
 MODULE_LICENSE("GPL v2");
@@ -205,40 +206,53 @@ static void init_can_skb_reserve(struct sk_buff *skb)
 }
 
 struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 
 	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
 			       sizeof(struct can_frame));
-	if (unlikely(!skb)) {
-		*cf = NULL;
+	if (unlikely(!skb))
+		goto out_error_cc;
 
-		return NULL;
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto out_error_cc;
 	}
 
 	skb->protocol = htons(ETH_P_CAN);
 	init_can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
 	*cf = skb_put_zero(skb, sizeof(struct can_frame));
 
 	return skb;
+
+out_error_cc:
+	*cf = NULL;
+
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(alloc_can_skb);
 
 struct sk_buff *alloc_canfd_skb(struct net_device *dev,
 				struct canfd_frame **cfd)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 
 	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
 			       sizeof(struct canfd_frame));
-	if (unlikely(!skb)) {
-		*cfd = NULL;
+	if (unlikely(!skb))
+		goto out_error_fd;
 
-		return NULL;
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto out_error_fd;
 	}
 
 	skb->protocol = htons(ETH_P_CANFD);
 	init_can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
@@ -247,26 +261,38 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
 
 	/* set CAN FD flag by default */
 	(*cfd)->flags = CANFD_FDF;
 
 	return skb;
+
+out_error_fd:
+	*cfd = NULL;
+
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(alloc_canfd_skb);
 
 struct sk_buff *alloc_canxl_skb(struct net_device *dev,
 				struct canxl_frame **cxl,
 				unsigned int data_len)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 
 	if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN)
-		goto out_error;
+		goto out_error_xl;
 
 	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
 			       CANXL_HDR_SIZE + data_len);
 	if (unlikely(!skb))
-		goto out_error;
+		goto out_error_xl;
+
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto out_error_xl;
+	}
 
 	skb->protocol = htons(ETH_P_CANXL);
 	init_can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
@@ -276,11 +302,11 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
 	(*cxl)->flags = CANXL_XLF;
 	(*cxl)->len = data_len;
 
 	return skb;
 
-out_error:
+out_error_xl:
 	*cxl = NULL;
 
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(alloc_canxl_skb);
@@ -301,17 +327,25 @@ struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
 EXPORT_SYMBOL_GPL(alloc_can_err_skb);
 
 /* Check for outgoing skbs that have not been created by the CAN subsystem */
 static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
 {
+	struct can_skb_ext *csx = can_skb_ext_find(skb);
+
 	/* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */
 	if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv)))
 		return false;
 
 	/* af_packet does not apply CAN skb specific settings */
-	if (skb->ip_summed == CHECKSUM_NONE) {
-		/* init headroom */
+	if (skb->ip_summed == CHECKSUM_NONE || !csx) {
+		/* init CAN skb content */
+		if (!csx) {
+			csx = can_skb_ext_add(skb);
+			if (!csx)
+				return false;
+		}
+
 		can_skb_prv(skb)->ifindex = dev->ifindex;
 
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 
 		/* perform proper loopback on capable devices */
diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c
index f14c6f02b662..53d7d9046f85 100644
--- a/drivers/net/can/vxcan.c
+++ b/drivers/net/can/vxcan.c
@@ -19,10 +19,11 @@
 #include <linux/can/dev.h>
 #include <linux/can/skb.h>
 #include <linux/can/vxcan.h>
 #include <linux/can/can-ml.h>
 #include <linux/slab.h>
+#include <net/can.h>
 #include <net/rtnetlink.h>
 
 #define DRV_NAME "vxcan"
 
 MODULE_DESCRIPTION("Virtual CAN Tunnel");
@@ -37,10 +38,11 @@ struct vxcan_priv {
 static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
 {
 	struct vxcan_priv *priv = netdev_priv(dev);
 	struct net_device *peer;
 	struct net_device_stats *peerstats, *srcstats = &dev->stats;
+	struct can_skb_ext *csx;
 	struct sk_buff *skb;
 	unsigned int len;
 
 	if (can_dropped_invalid_skb(dev, oskb))
 		return NETDEV_TX_OK;
@@ -61,10 +63,22 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
 	} else {
 		kfree_skb(oskb);
 		goto out_unlock;
 	}
 
+	/* the cloned skb points to the skb extension of the original oskb
+	 * with an increased refcount. skb_ext_add() creates a copy to
+	 * separate the skb extension data which is needed to start with a
+	 * fresh can_gw_hops counter in the other namespace.
+	 */
+	csx = skb_ext_add(skb, SKB_EXT_CAN);
+	if (!csx) {
+		kfree_skb(skb);
+		kfree_skb(oskb);
+		goto out_unlock;
+	}
+
 	/* reset CAN GW hop counter */
 	skb->csum_start = 0;
 	skb->pkt_type   = PACKET_BROADCAST;
 	skb->dev        = peer;
 	skb->ip_summed  = CHECKSUM_UNNECESSARY;
diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h
index 869ea574a40a..68c0f24e6914 100644
--- a/include/linux/can/skb.h
+++ b/include/linux/can/skb.h
@@ -12,10 +12,11 @@
 #define _CAN_SKB_H
 
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <linux/can.h>
+#include <net/can.h>
 #include <net/sock.h>
 
 void can_flush_echo_skb(struct net_device *dev);
 int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
 		     unsigned int idx, unsigned int frame_len);
@@ -66,10 +67,26 @@ static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb)
 static inline void can_skb_reserve(struct sk_buff *skb)
 {
 	skb_reserve(skb, sizeof(struct can_skb_priv));
 }
 
+static inline struct can_skb_ext *can_skb_ext_add(struct sk_buff *skb)
+{
+	struct can_skb_ext *csx = skb_ext_add(skb, SKB_EXT_CAN);
+
+	/* skb_ext_add() returns uninitialized space */
+	if (csx)
+		csx->can_gw_hops = 0;
+
+	return csx;
+}
+
+static inline struct can_skb_ext *can_skb_ext_find(struct sk_buff *skb)
+{
+	return skb_ext_find(skb, SKB_EXT_CAN);
+}
+
 static inline void can_skb_set_owner(struct sk_buff *skb, struct sock *sk)
 {
 	/* If the socket has already been closed by user space, the
 	 * refcount may already be 0 (and the socket will be freed
 	 * after the last TX skb has been freed). So only increase
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index e6bfe5d0c525..b5beb28e5730 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4986,10 +4986,13 @@ enum skb_ext_id {
 #if IS_ENABLED(CONFIG_MCTP_FLOWS)
 	SKB_EXT_MCTP,
 #endif
 #if IS_ENABLED(CONFIG_INET_PSP)
 	SKB_EXT_PSP,
+#endif
+#if IS_ENABLED(CONFIG_CAN)
+	SKB_EXT_CAN,
 #endif
 	SKB_EXT_NUM, /* must be last */
 };
 
 /**
diff --git a/include/net/can.h b/include/net/can.h
new file mode 100644
index 000000000000..6db9e826f0e0
--- /dev/null
+++ b/include/net/can.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * net/can.h
+ *
+ * Definitions for the CAN network socket buffer extensions
+ *
+ * Copyright (C) 2026 Oliver Hartkopp <socketcan@hartkopp.net>
+ *
+ */
+
+#ifndef _NET_CAN_H
+#define _NET_CAN_H
+
+/**
+ * struct can_skb_ext - skb extensions for CAN specific content
+ * @can_iif: ifindex of the first interface the CAN frame appeared on
+ * @can_framelen: cached echo CAN frame length for bql
+ * @can_gw_hops: can-gw CAN frame time-to-live counter
+ * @can_ext_flags: CAN skb extensions flags
+ */
+struct can_skb_ext {
+	int	can_iif;
+	u16	can_framelen;
+	u8	can_gw_hops;
+	u8	can_ext_flags;
+};
+
+#endif /* _NET_CAN_H */
diff --git a/net/can/Kconfig b/net/can/Kconfig
index af64a6f76458..abbb4be7ad21 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -3,10 +3,11 @@
 # Controller Area Network (CAN) network layer core configuration
 #
 
 menuconfig CAN
 	tristate "CAN bus subsystem support"
+	select SKB_EXTENSIONS
 	help
 	  Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
 	  communications protocol. Development of the CAN bus started in
 	  1983 at Robert Bosch GmbH, and the protocol was officially
 	  released in 1986. The CAN bus was originally mainly for automotive,
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 70659987ef4d..22c65a014861 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -685,11 +685,12 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
 }
 
 static int can_rcv(struct sk_buff *skb, struct net_device *dev,
 		   struct packet_type *pt, struct net_device *orig_dev)
 {
-	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_can_skb(skb))) {
+	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
+		     !can_skb_ext_find(skb) || !can_is_can_skb(skb))) {
 		pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n",
 			     dev->type, skb->len);
 
 		kfree_skb_reason(skb, SKB_DROP_REASON_CAN_RX_INVALID_FRAME);
 		return NET_RX_DROP;
@@ -700,11 +701,12 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
 }
 
 static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
 		     struct packet_type *pt, struct net_device *orig_dev)
 {
-	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_canfd_skb(skb))) {
+	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
+		     !can_skb_ext_find(skb) || !can_is_canfd_skb(skb))) {
 		pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n",
 			     dev->type, skb->len);
 
 		kfree_skb_reason(skb, SKB_DROP_REASON_CANFD_RX_INVALID_FRAME);
 		return NET_RX_DROP;
@@ -715,11 +717,12 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
 }
 
 static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,
 		     struct packet_type *pt, struct net_device *orig_dev)
 {
-	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_canxl_skb(skb))) {
+	if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
+		     !can_skb_ext_find(skb) || !can_is_canxl_skb(skb))) {
 		pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n",
 			     dev->type, skb->len);
 
 		kfree_skb_reason(skb, SKB_DROP_REASON_CANXL_RX_INVALID_FRAME);
 		return NET_RX_DROP;
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 8ed60f18c2ea..38452069dea8 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -57,10 +57,11 @@
 #include <linux/can/core.h>
 #include <linux/can/skb.h>
 #include <linux/can/bcm.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <net/can.h>
 #include <net/sock.h>
 #include <net/net_namespace.h>
 
 /*
  * To send multiple CAN frame content within TX_SETUP or to filter
@@ -289,10 +290,11 @@ static int bcm_proc_show(struct seq_file *m, void *v)
  *              of the given bcm tx op
  */
 static void bcm_can_tx(struct bcm_op *op)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct net_device *dev;
 	struct canfd_frame *cf;
 	int err;
 
 	/* no target device? => exit */
@@ -312,10 +314,16 @@ static void bcm_can_tx(struct bcm_op *op)
 
 	skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any());
 	if (!skb)
 		goto out;
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto out;
+	}
+
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
 	skb_put_data(skb, cf, op->cfsiz);
 
@@ -1315,10 +1323,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
  */
 static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
 		       int cfsiz)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct net_device *dev;
 	int err;
 
 	/* we need a real device to send frames */
 	if (!ifindex)
@@ -1326,10 +1335,16 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
 
 	skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), GFP_KERNEL);
 	if (!skb)
 		return -ENOMEM;
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
 	can_skb_reserve(skb);
 
 	err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz);
 	if (err < 0) {
 		kfree_skb(skb);
diff --git a/net/can/gw.c b/net/can/gw.c
index 55eccb1c7620..191afe3b673c 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -53,10 +53,11 @@
 #include <linux/skbuff.h>
 #include <linux/can.h>
 #include <linux/can/core.h>
 #include <linux/can/skb.h>
 #include <linux/can/gw.h>
+#include <net/can.h>
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
 
 #define CAN_GW_NAME "can-gw"
@@ -457,10 +458,11 @@ static void cgw_csum_crc8_neg(struct canfd_frame *cf,
 static void can_can_gw_rcv(struct sk_buff *skb, void *data)
 {
 	struct cgw_job *gwj = (struct cgw_job *)data;
 	struct canfd_frame *cf;
 	struct sk_buff *nskb;
+	struct can_skb_ext *csx, *ncsx;
 	struct cf_mod *mod;
 	int modidx = 0;
 
 	/* process strictly Classic CAN or CAN FD frames */
 	if (gwj->flags & CGW_FLAGS_CAN_FD) {
@@ -469,10 +471,14 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
 	} else {
 		if (!can_is_can_skb(skb))
 			return;
 	}
 
+	csx = can_skb_ext_find(skb);
+	if (!csx)
+		return;
+
 	/* Do not handle CAN frames routed more than 'max_hops' times.
 	 * In general we should never catch this delimiter which is intended
 	 * to cover a misconfiguration protection (e.g. circular CAN routes).
 	 *
 	 * The Controller Area Network controllers only accept CAN frames with
@@ -516,10 +522,21 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
 	if (!nskb) {
 		gwj->dropped_frames++;
 		return;
 	}
 
+	/* the cloned/copied nskb points to the skb extension of the original
+	 * skb with an increased refcount. skb_ext_add() creates a copy to
+	 * separate the skb extension data to modify the can_gw_hops.
+	 */
+	ncsx = skb_ext_add(nskb, SKB_EXT_CAN);
+	if (!ncsx) {
+		kfree_skb(nskb);
+		gwj->dropped_frames++;
+		return;
+	}
+
 	/* put the incremented hop counter in the cloned skb */
 	cgw_hops(nskb) = cgw_hops(skb) + 1;
 
 	/* first processing of this CAN frame -> adjust to private hop limit */
 	if (gwj->limit_hops && cgw_hops(nskb) == 1)
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 4bb60b8f9b96..94103fe654ff 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -67,10 +67,11 @@
 #include <linux/can.h>
 #include <linux/can/core.h>
 #include <linux/can/skb.h>
 #include <linux/can/isotp.h>
 #include <linux/slab.h>
+#include <net/can.h>
 #include <net/sock.h>
 #include <net/net_namespace.h>
 
 MODULE_DESCRIPTION("PF_CAN ISO 15765-2 transport protocol");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -212,18 +213,25 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer)
 
 static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
 {
 	struct net_device *dev;
 	struct sk_buff *nskb;
+	struct can_skb_ext *csx;
 	struct canfd_frame *ncf;
 	struct isotp_sock *so = isotp_sk(sk);
 	int can_send_ret;
 
 	nskb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), gfp_any());
 	if (!nskb)
 		return 1;
 
+	csx = can_skb_ext_add(nskb);
+	if (!csx) {
+		kfree_skb(nskb);
+		return 1;
+	}
+
 	dev = dev_get_by_index(sock_net(sk), so->ifindex);
 	if (!dev) {
 		kfree_skb(nskb);
 		return 1;
 	}
@@ -760,10 +768,11 @@ static void isotp_fill_dataframe(struct canfd_frame *cf, struct isotp_sock *so,
 
 static void isotp_send_cframe(struct isotp_sock *so)
 {
 	struct sock *sk = &so->sk;
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct net_device *dev;
 	struct canfd_frame *cf;
 	int can_send_ret;
 	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
 
@@ -775,10 +784,17 @@ static void isotp_send_cframe(struct isotp_sock *so)
 	if (!skb) {
 		dev_put(dev);
 		return;
 	}
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		netdev_put(dev, NULL);
+		return;
+	}
+
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
 	cf = (struct canfd_frame *)skb->data;
 	skb_put_zero(skb, so->ll.mtu);
@@ -936,10 +952,11 @@ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
 static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 {
 	struct sock *sk = sock->sk;
 	struct isotp_sock *so = isotp_sk(sk);
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct net_device *dev;
 	struct canfd_frame *cf;
 	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
 	int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
 	s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
@@ -1003,10 +1020,17 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 	if (!skb) {
 		dev_put(dev);
 		goto err_out_drop;
 	}
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		netdev_put(dev, NULL);
+		goto err_out_drop;
+	}
+
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
 	so->tx.len = size;
 	so->tx.idx = 0;
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
index 1589e8ca634e..fc28a7677369 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -15,10 +15,11 @@
 #include <linux/can/can-ml.h>
 #include <linux/can/core.h>
 #include <linux/can/skb.h>
 #include <linux/errqueue.h>
 #include <linux/if_arp.h>
+#include <net/can.h>
 
 #include "j1939-priv.h"
 
 #define J1939_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.j1939)
 
@@ -882,10 +883,11 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
 					  int *errcode)
 {
 	struct j1939_sock *jsk = j1939_sk(sk);
 	struct j1939_sk_buff_cb *skcb;
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	int ret;
 
 	skb = sock_alloc_send_skb(sk,
 				  size +
 				  sizeof(struct can_frame) -
@@ -893,10 +895,16 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
 				  sizeof(struct can_skb_priv),
 				  msg->msg_flags & MSG_DONTWAIT, &ret);
 	if (!skb)
 		goto failure;
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto failure;
+	}
+
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = ndev->ifindex;
 	skb_reserve(skb, offsetof(struct can_frame, data));
 
 	ret = memcpy_from_msg(skb_put(skb, size), msg, size);
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index d5d3e5320f7a..16b4ea5e31ff 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -7,10 +7,11 @@
 //                         Marc Kleine-Budde <kernel@pengutronix.de>
 // Copyright (c) 2017-2019 Pengutronix,
 //                         Oleksij Rempel <kernel@pengutronix.de>
 
 #include <linux/can/skb.h>
+#include <net/can.h>
 
 #include "j1939-priv.h"
 
 #define J1939_XTP_TX_RETRY_LIMIT 100
 
@@ -589,17 +590,24 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv,
 			     const struct j1939_sk_buff_cb *re_skcb,
 			     bool ctl,
 			     bool swap_src_dst)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct j1939_sk_buff_cb *skcb;
 
 	skb = alloc_skb(sizeof(struct can_frame) + sizeof(struct can_skb_priv),
 			GFP_ATOMIC);
 	if (unlikely(!skb))
 		return ERR_PTR(-ENOMEM);
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		return ERR_PTR(-ENOMEM);
+	}
+
 	skb->dev = priv->ndev;
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = priv->ndev->ifindex;
 	/* reserve CAN header */
 	skb_reserve(skb, offsetof(struct can_frame, data));
@@ -1049,11 +1057,22 @@ static int j1939_simple_txnext(struct j1939_session *session)
 	if (!skb) {
 		ret = -ENOMEM;
 		goto out_free;
 	}
 
-	can_skb_set_owner(skb, se_skb->sk);
+	/* the cloned skb points to the skb extension of the original se_skb
+	 * with an increased refcount. skb_ext_add() creates a copy to
+	 * separate the skb extension data which is needed to modify the
+	 * can_framelen in can_put_echo_skb().
+	 */
+	if (!skb_ext_add(skb, SKB_EXT_CAN)) {
+		kfree_skb(skb);
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+       can_skb_set_owner(skb, se_skb->sk);
 
 	j1939_tp_set_rxtimeout(session, J1939_SIMPLE_ECHO_TIMEOUT_MS);
 
 	ret = j1939_send_one(priv, skb);
 	if (ret)
@@ -1523,17 +1542,24 @@ static struct
 j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
 				       int size,
 				       const struct j1939_sk_buff_cb *rel_skcb)
 {
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct j1939_sk_buff_cb *skcb;
 	struct j1939_session *session;
 
 	skb = alloc_skb(size + sizeof(struct can_skb_priv), GFP_ATOMIC);
 	if (unlikely(!skb))
 		return NULL;
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		return NULL;
+	}
+
 	skb->dev = priv->ndev;
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = priv->ndev->ifindex;
 	skcb = j1939_skb_to_cb(skb);
 	memcpy(skcb, rel_skcb, sizeof(*skcb));
diff --git a/net/can/raw.c b/net/can/raw.c
index fb4f9c854df0..03438e9bc535 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -51,10 +51,11 @@
 #include <linux/can.h>
 #include <linux/can/can-ml.h>
 #include <linux/can/core.h>
 #include <linux/can/skb.h>
 #include <linux/can/raw.h>
+#include <net/can.h>
 #include <net/sock.h>
 #include <net/net_namespace.h>
 
 MODULE_DESCRIPTION("PF_CAN raw protocol");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -916,10 +917,11 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 {
 	struct sock *sk = sock->sk;
 	struct raw_sock *ro = raw_sk(sk);
 	struct sockcm_cookie sockc;
 	struct sk_buff *skb;
+	struct can_skb_ext *csx;
 	struct net_device *dev;
 	unsigned int txmtu;
 	int ifindex;
 	int err = -EINVAL;
 
@@ -954,10 +956,16 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 	skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		goto put_dev;
 
+	csx = can_skb_ext_add(skb);
+	if (!csx) {
+		kfree_skb(skb);
+		goto put_dev;
+	}
+
 	can_skb_reserve(skb);
 	can_skb_prv(skb)->ifindex = dev->ifindex;
 
 	/* fill the skb before testing for valid CAN frames */
 	err = memcpy_from_msg(skb_put(skb, size), msg, size);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 4d3920e5b141..648c20e19038 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -76,10 +76,11 @@
 #include <net/ip6_checksum.h>
 #include <net/xfrm.h>
 #include <net/mpls.h>
 #include <net/mptcp.h>
 #include <net/mctp.h>
+#include <net/can.h>
 #include <net/page_pool/helpers.h>
 #include <net/psp/types.h>
 #include <net/dropreason.h>
 #include <net/xdp_sock.h>
 
@@ -5137,10 +5138,13 @@ static const u8 skb_ext_type_len[] = {
 	[SKB_EXT_MCTP] = SKB_EXT_CHUNKSIZEOF(struct mctp_flow),
 #endif
 #if IS_ENABLED(CONFIG_INET_PSP)
 	[SKB_EXT_PSP] = SKB_EXT_CHUNKSIZEOF(struct psp_skb_ext),
 #endif
+#if IS_ENABLED(CONFIG_CAN)
+	[SKB_EXT_CAN] = SKB_EXT_CHUNKSIZEOF(struct can_skb_ext),
+#endif
 };
 
 static __always_inline unsigned int skb_ext_total_length(void)
 {
 	unsigned int l = SKB_EXT_CHUNKSIZEOF(struct skb_ext);

-- 
2.51.0
Re: [PATCH net-next v7 2/6] can: add CAN skb extension infrastructure
Posted by kernel test robot 1 week, 2 days ago
Hi Oliver,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 239f09e258b906deced5c2a7c1ac8aed301b558b]

url:    https://github.com/intel-lab-lkp/linux/commits/Oliver-Hartkopp-via-B4-Relay/can-use-skb-hash-instead-of-private-variable-in-headroom/20260131-212921
base:   239f09e258b906deced5c2a7c1ac8aed301b558b
patch link:    https://lore.kernel.org/r/20260131-can_skb_ext-v7-2-dd0f8f84a83d%40hartkopp.net
patch subject: [PATCH net-next v7 2/6] can: add CAN skb extension infrastructure
config: s390-randconfig-r072-20260201 (https://download.01.org/0day-ci/archive/20260201/202602010426.PnGrYAk3-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
smatch version: v0.5.0-8994-gd50c5a4c

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602010426.PnGrYAk3-lkp@intel.com/

smatch warnings:
net/can/j1939/transport.c:1073 j1939_simple_txnext() warn: inconsistent indenting

vim +1073 net/can/j1939/transport.c

9d71dd0c700999 The j1939 authors 2018-10-08  1045  
9d71dd0c700999 The j1939 authors 2018-10-08  1046  static int j1939_simple_txnext(struct j1939_session *session)
9d71dd0c700999 The j1939 authors 2018-10-08  1047  {
9d71dd0c700999 The j1939 authors 2018-10-08  1048  	struct j1939_priv *priv = session->priv;
2030043e616cab Oleksij Rempel    2021-05-21  1049  	struct sk_buff *se_skb = j1939_session_skb_get(session);
9d71dd0c700999 The j1939 authors 2018-10-08  1050  	struct sk_buff *skb;
9d71dd0c700999 The j1939 authors 2018-10-08  1051  	int ret;
9d71dd0c700999 The j1939 authors 2018-10-08  1052  
9d71dd0c700999 The j1939 authors 2018-10-08  1053  	if (!se_skb)
9d71dd0c700999 The j1939 authors 2018-10-08  1054  		return 0;
9d71dd0c700999 The j1939 authors 2018-10-08  1055  
9d71dd0c700999 The j1939 authors 2018-10-08  1056  	skb = skb_clone(se_skb, GFP_ATOMIC);
2030043e616cab Oleksij Rempel    2021-05-21  1057  	if (!skb) {
2030043e616cab Oleksij Rempel    2021-05-21  1058  		ret = -ENOMEM;
2030043e616cab Oleksij Rempel    2021-05-21  1059  		goto out_free;
2030043e616cab Oleksij Rempel    2021-05-21  1060  	}
9d71dd0c700999 The j1939 authors 2018-10-08  1061  
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1062  	/* the cloned skb points to the skb extension of the original se_skb
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1063  	 * with an increased refcount. skb_ext_add() creates a copy to
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1064  	 * separate the skb extension data which is needed to modify the
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1065  	 * can_framelen in can_put_echo_skb().
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1066  	 */
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1067  	if (!skb_ext_add(skb, SKB_EXT_CAN)) {
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1068  		kfree_skb(skb);
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1069  		ret = -ENOMEM;
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1070  		goto out_free;
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1071  	}
f170b16e1461f2 Oliver Hartkopp   2026-01-31  1072  
9d71dd0c700999 The j1939 authors 2018-10-08 @1073         can_skb_set_owner(skb, se_skb->sk);
9d71dd0c700999 The j1939 authors 2018-10-08  1074  
9d71dd0c700999 The j1939 authors 2018-10-08  1075  	j1939_tp_set_rxtimeout(session, J1939_SIMPLE_ECHO_TIMEOUT_MS);
9d71dd0c700999 The j1939 authors 2018-10-08  1076  
9d71dd0c700999 The j1939 authors 2018-10-08  1077  	ret = j1939_send_one(priv, skb);
9d71dd0c700999 The j1939 authors 2018-10-08  1078  	if (ret)
2030043e616cab Oleksij Rempel    2021-05-21  1079  		goto out_free;
9d71dd0c700999 The j1939 authors 2018-10-08  1080  
cd85d3aed5cf44 Oleksij Rempel    2021-07-07  1081  	j1939_sk_errqueue(session, J1939_ERRQUEUE_TX_SCHED);
9d71dd0c700999 The j1939 authors 2018-10-08  1082  	j1939_sk_queue_activate_next(session);
9d71dd0c700999 The j1939 authors 2018-10-08  1083  
2030043e616cab Oleksij Rempel    2021-05-21  1084   out_free:
2030043e616cab Oleksij Rempel    2021-05-21  1085  	if (ret)
2030043e616cab Oleksij Rempel    2021-05-21  1086  		kfree_skb(se_skb);
2030043e616cab Oleksij Rempel    2021-05-21  1087  	else
2030043e616cab Oleksij Rempel    2021-05-21  1088  		consume_skb(se_skb);
2030043e616cab Oleksij Rempel    2021-05-21  1089  
2030043e616cab Oleksij Rempel    2021-05-21  1090  	return ret;
9d71dd0c700999 The j1939 authors 2018-10-08  1091  }
9d71dd0c700999 The j1939 authors 2018-10-08  1092  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [net-next,v7,2/6] can: add CAN skb extension infrastructure
Posted by Jakub Kicinski 1 week, 2 days ago
This is an AI-generated review of your patch. The human sending this
email says:

  Not sure about the first issue, I think you mentioned we have
  an extra ref? The uninitialized errors should probably be fixed
  "in net", before this series? :(

For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
can: add CAN skb extension infrastructure

This patch introduces CAN skb extension infrastructure to store per-frame
metadata (interface index, frame length, gateway hops, flags) using the
standard skb_ext mechanism instead of the private CAN bus skb headroom.

> diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c
> index f14c6f02b662..53d7d9046f85 100644
> --- a/drivers/net/can/vxcan.c
> +++ b/drivers/net/can/vxcan.c

[ ... ]

> @@ -39,6 +40,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
>  	struct vxcan_priv *priv = netdev_priv(dev);
>  	struct net_device *peer;
>  	struct net_device_stats *peerstats, *srcstats = &dev->stats;
> +	struct can_skb_ext *csx;
>  	struct sk_buff *skb;
>  	unsigned int len;
>
> @@ -63,6 +65,18 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
>  		goto out_unlock;
>  	}
>
> +	/* the cloned skb points to the skb extension of the original oskb
> +	 * with an increased refcount. skb_ext_add() creates a copy to
> +	 * separate the skb extension data which is needed to start with a
> +	 * fresh can_gw_hops counter in the other namespace.
> +	 */
> +	csx = skb_ext_add(skb, SKB_EXT_CAN);
> +	if (!csx) {
> +		kfree_skb(skb);
> +		kfree_skb(oskb);
                ^^^^^^^^^^^^^^^^

Is this a double-free of oskb? Looking at the code flow:

    skb = skb_clone(oskb, GFP_ATOMIC);  /* line 60 */
    if (unlikely(!skb)) { ... }
    consume_skb(oskb);                   /* line 62 - frees oskb */

At this point oskb has been freed by consume_skb(). If skb_ext_add() then
fails, the error handler calls kfree_skb(oskb) on the already-freed pointer.

This can occur under memory pressure when skb_clone() succeeds but the
subsequent skb_ext_add() allocation fails. The kfree_skb(oskb) line should
be removed from this error path.

> +		goto out_unlock;
> +	}

[ ... ]

> diff --git a/net/can/isotp.c b/net/can/isotp.c
> index 4bb60b8f9b96..94103fe654ff 100644
> --- a/net/can/isotp.c
> +++ b/net/can/isotp.c

[ ... ]

> @@ -1005,6 +1022,13 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
>  		goto err_out_drop;
>  	}
>
> +	csx = can_skb_ext_add(skb);
> +	if (!csx) {
> +		kfree_skb(skb);
> +		netdev_put(dev, NULL);
> +		goto err_out_drop;
> +	}

Does this error path return an uninitialized value to userspace? The variable
err is declared without initialization at line 959 and sock_alloc_send_skb()
only sets it on failure. When can_skb_ext_add() fails after a successful
sock_alloc_send_skb(), err contains garbage.

Other error paths in this function set err explicitly before jumping to
err_out_drop. Should this add err = -ENOMEM before the goto?

> diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
> index 1589e8ca634e..fc28a7677369 100644
> --- a/net/can/j1939/socket.c
> +++ b/net/can/j1939/socket.c

[ ... ]

> @@ -895,6 +897,12 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
>  	if (!skb)
>  		goto failure;
>
> +	csx = can_skb_ext_add(skb);
> +	if (!csx) {
> +		kfree_skb(skb);
> +		goto failure;
> +	}

Similar concern here: ret is declared without initialization at line 891.
sock_alloc_send_skb() only writes to ret on failure. When can_skb_ext_add()
fails after successful allocation, ret is uninitialized when returned via
*errcode at the failure label.

Should this set ret = -ENOMEM before goto failure?

> diff --git a/net/can/raw.c b/net/can/raw.c
> index fb4f9c854df0..03438e9bc535 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c

[ ... ]

> @@ -956,6 +958,12 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
>  	if (!skb)
>  		goto put_dev;
>
> +	csx = can_skb_ext_add(skb);
> +	if (!csx) {
> +		kfree_skb(skb);
> +		goto put_dev;
> +	}

This returns -EINVAL (from line 923) when can_skb_ext_add() fails due to
memory allocation failure. Should this be -ENOMEM instead? The
sock_alloc_send_skb() above this code returns -ENOMEM for allocation
failures.
-- 
pw-bot: cr
Re: [net-next,v7,2/6] can: add CAN skb extension infrastructure
Posted by Oliver Hartkopp 1 week, 1 day ago
On 31.01.26 19:00, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says:
> 
>    Not sure about the first issue, I think you mentioned we have
>    an extra ref? The uninitialized errors should probably be fixed
>    "in net", before this series? :(

Fortunately there have not been uninitialized ret values before.
E.g. the sock_alloc_send_skb( ..., &err) calls set the ret val before 
going to the out/err labels.

In the other goto/return cases the existing code sets the values explicitly.

Therefore setting the missing err values within the newly added code 
completely addresses the uninitialized ret value problems.

Additionally the kernel test robot discovered an indention issue in the 
original j1939 code from 2018 which is no candidate for net/fixes either 
and can be silently corrected.

I'll send a v8 patch set when the 24h grace period is over 8-)

Best regards,
Oliver

> 
> For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
> ---
> can: add CAN skb extension infrastructure
> 
> This patch introduces CAN skb extension infrastructure to store per-frame
> metadata (interface index, frame length, gateway hops, flags) using the
> standard skb_ext mechanism instead of the private CAN bus skb headroom.
> 
>> diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c
>> index f14c6f02b662..53d7d9046f85 100644
>> --- a/drivers/net/can/vxcan.c
>> +++ b/drivers/net/can/vxcan.c
> 
> [ ... ]
> 
>> @@ -39,6 +40,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
>>   	struct vxcan_priv *priv = netdev_priv(dev);
>>   	struct net_device *peer;
>>   	struct net_device_stats *peerstats, *srcstats = &dev->stats;
>> +	struct can_skb_ext *csx;
>>   	struct sk_buff *skb;
>>   	unsigned int len;
>>
>> @@ -63,6 +65,18 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
>>   		goto out_unlock;
>>   	}
>>
>> +	/* the cloned skb points to the skb extension of the original oskb
>> +	 * with an increased refcount. skb_ext_add() creates a copy to
>> +	 * separate the skb extension data which is needed to start with a
>> +	 * fresh can_gw_hops counter in the other namespace.
>> +	 */
>> +	csx = skb_ext_add(skb, SKB_EXT_CAN);
>> +	if (!csx) {
>> +		kfree_skb(skb);
>> +		kfree_skb(oskb);
>                  ^^^^^^^^^^^^^^^^
> 
> Is this a double-free of oskb? Looking at the code flow:
> 
>      skb = skb_clone(oskb, GFP_ATOMIC);  /* line 60 */
>      if (unlikely(!skb)) { ... }
>      consume_skb(oskb);                   /* line 62 - frees oskb */
> 
> At this point oskb has been freed by consume_skb(). If skb_ext_add() then
> fails, the error handler calls kfree_skb(oskb) on the already-freed pointer.
> 
> This can occur under memory pressure when skb_clone() succeeds but the
> subsequent skb_ext_add() allocation fails. The kfree_skb(oskb) line should
> be removed from this error path.
> 
>> +		goto out_unlock;
>> +	}
> 
> [ ... ]
> 
>> diff --git a/net/can/isotp.c b/net/can/isotp.c
>> index 4bb60b8f9b96..94103fe654ff 100644
>> --- a/net/can/isotp.c
>> +++ b/net/can/isotp.c
> 
> [ ... ]
> 
>> @@ -1005,6 +1022,13 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
>>   		goto err_out_drop;
>>   	}
>>
>> +	csx = can_skb_ext_add(skb);
>> +	if (!csx) {
>> +		kfree_skb(skb);
>> +		netdev_put(dev, NULL);
>> +		goto err_out_drop;
>> +	}
> 
> Does this error path return an uninitialized value to userspace? The variable
> err is declared without initialization at line 959 and sock_alloc_send_skb()
> only sets it on failure. When can_skb_ext_add() fails after a successful
> sock_alloc_send_skb(), err contains garbage.
> 
> Other error paths in this function set err explicitly before jumping to
> err_out_drop. Should this add err = -ENOMEM before the goto?
> 
>> diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
>> index 1589e8ca634e..fc28a7677369 100644
>> --- a/net/can/j1939/socket.c
>> +++ b/net/can/j1939/socket.c
> 
> [ ... ]
> 
>> @@ -895,6 +897,12 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
>>   	if (!skb)
>>   		goto failure;
>>
>> +	csx = can_skb_ext_add(skb);
>> +	if (!csx) {
>> +		kfree_skb(skb);
>> +		goto failure;
>> +	}
> 
> Similar concern here: ret is declared without initialization at line 891.
> sock_alloc_send_skb() only writes to ret on failure. When can_skb_ext_add()
> fails after successful allocation, ret is uninitialized when returned via
> *errcode at the failure label.
> 
> Should this set ret = -ENOMEM before goto failure?
> 
>> diff --git a/net/can/raw.c b/net/can/raw.c
>> index fb4f9c854df0..03438e9bc535 100644
>> --- a/net/can/raw.c
>> +++ b/net/can/raw.c
> 
> [ ... ]
> 
>> @@ -956,6 +958,12 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
>>   	if (!skb)
>>   		goto put_dev;
>>
>> +	csx = can_skb_ext_add(skb);
>> +	if (!csx) {
>> +		kfree_skb(skb);
>> +		goto put_dev;
>> +	}
> 
> This returns -EINVAL (from line 923) when can_skb_ext_add() fails due to
> memory allocation failure. Should this be -ENOMEM instead? The
> sock_alloc_send_skb() above this code returns -ENOMEM for allocation
> failures.