| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/22 11:37 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": [
"CAN",
"CAN_RAW",
"CAN_BCM",
"CAN_ISOTP",
"CAN_GW",
"CAN_J1939",
"CAN_DEV",
"CAN_VCAN",
"CAN_VXCAN"
],
"FocusSymbols": [
"can_rx_register",
"can_rx_unregister",
"raw_bind",
"raw_release",
"raw_setsockopt",
"raw_notify",
"isotp_bind",
"isotp_release",
"isotp_notify",
"isotp_rcv",
"bcm_release",
"bcm_rx_unreg",
"can_dropped_invalid_skb",
"can_put_echo_skb"
],
"KMSANReasoning": "The patch series refactors CAN networking subsystems (af_can, raw, isotp, bcm, gw, j1939):\n1. Locking \u0026 Concurrency: Replaces RTNL locking with socket locks, RCU device lookups, and per-device receive list spinlocks (dev_rcv_lists-\u003elock). Fixes concurrency races between socket operations and netdev notifier events. Potential bugs here are deadlocks, races, or use-after-free, which are covered by LOCKDEP and KASAN.\n2. Memory Freeing: Replaces kfree_skb/consume_skb with dev_kfree_skb_any/dev_consume_skb_any to ensure safe invocation from atomic or IRQ contexts.\n3. Procfs \u0026 Identifiers: Replaces kernel pointer/ident prints with socket inode numbers (sock_i_ino) and symbol formatting (%ps) in /proc/net/can/rcvlist_*. All fields printed or formatted are explicitly initialized, and all new struct fields (e.g. rcv-\u003eino, ro-\u003ebind_dev, dev_rcv_lists-\u003elock) are fully initialized upon allocation.\n4. Statistics \u0026 Checks: Replaces memset with an explicit can_reset_pkg_stats() that resets all fields of can_pkg_stats, and adds stricter CAN frame validation (can_is_can_skb / CAN_CAP_XL).\n5. setsockopt: CAN_RAW_FILTER copying is moved outside lock_sock; sfilter and dynamically allocated filters are strictly initialized before being linked or read.\n\nNone of the changes introduce risks of uninitialized memory reads, uninitialized structure padding leaks to user space, or branching on uninitialized data. A dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the Linux CAN subsystem (af_can, can-raw, can-isotp, can-bcm, and can-dev). It introduces significant synchronization and locking refactorings: removing rtnl_lock from raw and isotp socket bind, release, and setsockopt; switching from a global rcvlists_lock to per-device spinlocks; introducing lockless RCU device lookups and memory barriers (smp_store_release, smp_load_acquire, smp_mb) for netdev notifier events; and handling cross-namespace device unregistration. These reachable paths are accessible via AF_CAN sockets and virtual CAN devices (vcan, vxcan), making the changes highly susceptible to race conditions, memory ordering errors, and deadlocks that warrant fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/22 11:37 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit b71beff35437da5a2578cb3ac13b10a616a1f59f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Oct 22 11:37:14 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/networking/can.rst b/Documentation/networking/can.rst\nindex 536ff411da1d1..51bc70748f753 100644\n--- a/Documentation/networking/can.rst\n+++ b/Documentation/networking/can.rst\n@@ -1040,17 +1040,21 @@ As described in :ref:`socketcan-receive-lists` the SocketCAN core uses several f\n lists to deliver received CAN frames to CAN protocol modules. These\n receive lists, their filters and the count of filter matches can be\n checked in the appropriate receive list. All entries contain the\n-device and a protocol module identifier::\n+CAN network device, the filter (can_id/can_mask), the number of filter\n+matches, the inode number of the subscribing socket and the name of the\n+receive function. A sock_inode of 0 denotes a kernel internal subscriber\n+without a socket (e.g. CAN gateway or J1939). When the function name can\n+not be resolved (e.g. with CONFIG_KALLSYMS=n) \"(unknown)\" is printed::\n \n foo@bar:~$ cat /proc/net/can/rcvlist_all\n \n receive list 'rx_all':\n- (vcan3: no entry)\n- (vcan2: no entry)\n- (vcan1: no entry)\n- device can_id can_mask function userdata matches ident\n- vcan0 000 00000000 f88e6370 f6c6f400 0 raw\n (any: no entry)\n+ device can_id can_mask matches sock_inode function\n+ vcan0 000 00000000 0 63586 raw_rcv [can_raw]\n+ (vcan1: no entry)\n+ (vcan2: no entry)\n+ (vcan3: no entry)\n \n In this example an application requests any CAN traffic from vcan0::\n \ndiff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c\nindex 14edec5afb57e..738d5968dfa68 100644\n--- a/drivers/net/can/dev/skb.c\n+++ b/drivers/net/can/dev/skb.c\n@@ -4,6 +4,7 @@\n * Copyright (C) 2008-2009 Wolfgang Grandegger \u003cwg@grandegger.com\u003e\n */\n \n+#include \u003clinux/can/can-ml.h\u003e\n #include \u003clinux/can/dev.h\u003e\n #include \u003clinux/module.h\u003e\n #include \u003cnet/can.h\u003e\n@@ -54,6 +55,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,\n \tif (idx \u003e= priv-\u003eecho_skb_max) {\n \t\tnetdev_err(dev, \"%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\\n\",\n \t\t\t __func__, idx, priv-\u003eecho_skb_max);\n+\t\tdev_kfree_skb_any(skb);\n \t\treturn -EINVAL;\n \t}\n \n@@ -62,7 +64,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,\n \t (skb-\u003eprotocol != htons(ETH_P_CAN) \u0026\u0026\n \t skb-\u003eprotocol != htons(ETH_P_CANFD) \u0026\u0026\n \t skb-\u003eprotocol != htons(ETH_P_CANXL))) {\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\treturn 0;\n \t}\n \n@@ -90,7 +92,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,\n \t} else {\n \t\t/* locking problem with netif_stop_queue() ?? */\n \t\tnetdev_err(dev, \"%s: BUG! echo_skb %d is occupied!\\n\", __func__, idx);\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\treturn -EBUSY;\n \t}\n \n@@ -227,7 +229,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)\n \n \tcsx = can_skb_ext_add(skb);\n \tif (!csx) {\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\tgoto out_error_cc;\n \t}\n \n@@ -258,7 +260,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,\n \n \tcsx = can_skb_ext_add(skb);\n \tif (!csx) {\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\tgoto out_error_fd;\n \t}\n \n@@ -296,7 +298,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,\n \n \tcsx = can_skb_ext_add(skb);\n \tif (!csx) {\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\tgoto out_error_xl;\n \t}\n \n@@ -388,7 +390,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)\n \t\tbreak;\n \n \tcase ETH_P_CANXL:\n-\t\tif (!can_is_canxl_skb(skb))\n+\t\tif (!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb))\n \t\t\tgoto inval_skb;\n \t\tbreak;\n \n@@ -402,7 +404,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)\n \treturn false;\n \n inval_skb:\n-\tkfree_skb(skb);\n+\tdev_kfree_skb_any(skb);\n \tdev-\u003estats.tx_dropped++;\n \treturn true;\n }\ndiff --git a/include/linux/can/can-ml.h b/include/linux/can/can-ml.h\nindex 1e99fda2b3803..cfefa4a385be9 100644\n--- a/include/linux/can/can-ml.h\n+++ b/include/linux/can/can-ml.h\n@@ -45,6 +45,7 @@\n #include \u003clinux/can.h\u003e\n #include \u003clinux/list.h\u003e\n #include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/spinlock.h\u003e\n \n /* exposed CAN device capabilities for network layer */\n #define CAN_CAP_CC BIT(0) /* CAN CC aka Classical CAN */\n@@ -59,6 +60,7 @@\n enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };\n \n struct can_dev_rcv_lists {\n+\tspinlock_t lock; /* protects the receive lists and entries */\n \tstruct hlist_head rx[RX_MAX];\n \tstruct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];\n \tstruct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];\n@@ -81,6 +83,7 @@ static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)\n static inline void can_set_ml_priv(struct net_device *dev,\n \t\t\t\t struct can_ml_priv *ml_priv)\n {\n+\tspin_lock_init(\u0026ml_priv-\u003edev_rcv_lists.lock);\n \tnetdev_set_ml_priv(dev, ml_priv, ML_PRIV_CAN);\n }\n \ndiff --git a/include/linux/can/core.h b/include/linux/can/core.h\nindex 2de74c2b78b6c..effb5c31ef401 100644\n--- a/include/linux/can/core.h\n+++ b/include/linux/can/core.h\n@@ -51,7 +51,7 @@ extern void can_proto_unregister(const struct can_proto *cp);\n int can_rx_register(struct net *net, struct net_device *dev,\n \t\t canid_t can_id, canid_t mask,\n \t\t void (*func)(struct sk_buff *, void *),\n-\t\t void *data, char *ident, struct sock *sk);\n+\t\t void *data, u64 ino, struct sock *sk);\n \n extern void can_rx_unregister(struct net *net, struct net_device *dev,\n \t\t\t canid_t can_id, canid_t mask,\ndiff --git a/include/linux/can/dev.h b/include/linux/can/dev.h\nindex 6d0710d6f571d..346faddee02eb 100644\n--- a/include/linux/can/dev.h\n+++ b/include/linux/can/dev.h\n@@ -176,7 +176,7 @@ static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *s\n \treturn can_dropped_invalid_skb(dev, skb);\n \n invalid_skb:\n-\tkfree_skb(skb);\n+\tdev_kfree_skb_any(skb);\n \tdev-\u003estats.tx_dropped++;\n \treturn true;\n }\ndiff --git a/include/linux/can/skb.h b/include/linux/can/skb.h\nindex 5d27843862fca..87ace09c0d8bd 100644\n--- a/include/linux/can/skb.h\n+++ b/include/linux/can/skb.h\n@@ -78,12 +78,12 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)\n \n \tnskb = skb_clone(skb, GFP_ATOMIC);\n \tif (unlikely(!nskb)) {\n-\t\tkfree_skb(skb);\n+\t\tdev_kfree_skb_any(skb);\n \t\treturn NULL;\n \t}\n \n \tcan_skb_set_owner(nskb, skb-\u003esk);\n-\tconsume_skb(skb);\n+\tdev_consume_skb_any(skb);\n \treturn nskb;\n }\n \ndiff --git a/include/net/netns/can.h b/include/net/netns/can.h\nindex 48b79f7e6236d..54f23681e0d45 100644\n--- a/include/net/netns/can.h\n+++ b/include/net/netns/can.h\n@@ -29,7 +29,7 @@ struct netns_can {\n \n \t/* receive filters subscribed for 'all' CAN devices */\n \tstruct can_dev_rcv_lists *rx_alldev_list;\n-\tspinlock_t rcvlists_lock;\n+\tspinlock_t rcvlists_lock; /* protects the rcv_entries(_max) stats */\n \tstruct timer_list stattimer; /* timer for statistics update */\n \tstruct can_pkg_stats *pkg_stats;\n \tstruct can_rcv_lists_stats *rcv_lists_stats;\ndiff --git a/net/can/af_can.c b/net/can/af_can.c\nindex 34fe3b28d576f..0f3051a61aab5 100644\n--- a/net/can/af_can.c\n+++ b/net/can/af_can.c\n@@ -226,7 +226,7 @@ int can_send(struct sk_buff *skb, int loop)\n \t\tgoto inval_skb;\n \t}\n \n-\tif (unlikely(skb-\u003edev-\u003etype != ARPHRD_CAN)) {\n+\tif (unlikely(!can_get_ml_priv(skb-\u003edev))) {\n \t\terr = -EPERM;\n \t\tgoto inval_skb;\n \t}\n@@ -418,7 +418,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,\n * @mask: CAN mask (see description)\n * @func: callback function on filter match\n * @data: returned parameter for callback function\n- * @ident: string for calling module identification\n+ * @ino: inode number of sock (0 = unknown)\n * @sk: socket pointer (might be NULL)\n *\n * Description:\n@@ -443,7 +443,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,\n */\n int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,\n \t\t canid_t mask, void (*func)(struct sk_buff *, void *),\n-\t\t void *data, char *ident, struct sock *sk)\n+\t\t void *data, u64 ino, struct sock *sk)\n {\n \tstruct receiver *rcv;\n \tstruct hlist_head *rcv_list;\n@@ -452,7 +452,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,\n \n \t/* insert new receiver (dev,canid,mask) -\u003e (func,data) */\n \n-\tif (dev \u0026\u0026 (dev-\u003etype != ARPHRD_CAN || !can_get_ml_priv(dev)))\n+\tif (dev \u0026\u0026 !can_get_ml_priv(dev))\n \t\treturn -ENODEV;\n \n \tif (dev \u0026\u0026 !net_eq(net, dev_net(dev)))\n@@ -462,9 +462,10 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,\n \tif (!rcv)\n \t\treturn -ENOMEM;\n \n-\tspin_lock_bh(\u0026net-\u003ecan.rcvlists_lock);\n-\n \tdev_rcv_lists = can_dev_rcv_lists_find(net, dev);\n+\n+\tspin_lock_bh(\u0026dev_rcv_lists-\u003elock);\n+\n \trcv_list = can_rcv_list_find(\u0026can_id, \u0026mask, dev_rcv_lists);\n \n \trcv-\u003ecan_id = can_id;\n@@ -472,12 +473,15 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,\n \tatomic_long_set(\u0026rcv-\u003ematches, 0);\n \trcv-\u003efunc = func;\n \trcv-\u003edata = data;\n-\trcv-\u003eident = ident;\n+\trcv-\u003eino = ino;\n \trcv-\u003esk = sk;\n \n \thlist_add_head_rcu(\u0026rcv-\u003elist, rcv_list);\n \tdev_rcv_lists-\u003eentries++;\n \n+\tspin_unlock_bh(\u0026dev_rcv_lists-\u003elock);\n+\n+\tspin_lock_bh(\u0026net-\u003ecan.rcvlists_lock);\n \trcv_lists_stats-\u003ercv_entries++;\n \trcv_lists_stats-\u003ercv_entries_max = max(rcv_lists_stats-\u003ercv_entries_max,\n \t\t\t\t\t rcv_lists_stats-\u003ercv_entries);\n@@ -509,6 +513,14 @@ static void can_rx_delete_receiver(struct rcu_head *rp)\n *\n * Description:\n * Removes subscription entry depending on given (subscription) values.\n+ * The receive lists of a CAN device are protected by a per-device lock,\n+ * so the subscription is found even when the device has been moved to\n+ * another network namespace after can_rx_register(). The caller has to\n+ * ensure that @dev stays valid, e.g. by holding a reference to @dev or\n+ * RTNL. @net should be the namespace that has been used for\n+ * can_rx_register(). It selects the 'any' CAN device lists for\n+ * @dev == NULL and is otherwise only used for the receive list\n+ * statistics, which become imprecise when another namespace is passed.\n */\n void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,\n \t\t canid_t mask, void (*func)(struct sk_buff *, void *),\n@@ -519,15 +531,13 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,\n \tstruct can_rcv_lists_stats *rcv_lists_stats = net-\u003ecan.rcv_lists_stats;\n \tstruct can_dev_rcv_lists *dev_rcv_lists;\n \n-\tif (dev \u0026\u0026 dev-\u003etype != ARPHRD_CAN)\n+\tif (dev \u0026\u0026 !can_get_ml_priv(dev))\n \t\treturn;\n \n-\tif (dev \u0026\u0026 !net_eq(net, dev_net(dev)))\n-\t\treturn;\n+\tdev_rcv_lists = can_dev_rcv_lists_find(net, dev);\n \n-\tspin_lock_bh(\u0026net-\u003ecan.rcvlists_lock);\n+\tspin_lock_bh(\u0026dev_rcv_lists-\u003elock);\n \n-\tdev_rcv_lists = can_dev_rcv_lists_find(net, dev);\n \trcv_list = can_rcv_list_find(\u0026can_id, \u0026mask, dev_rcv_lists);\n \n \t/* Search the receiver list for the item to delete. This should\n@@ -547,26 +557,26 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,\n \t * a warning here.\n \t */\n \tif (!rcv) {\n+\t\tspin_unlock_bh(\u0026dev_rcv_lists-\u003elock);\n \t\tpr_warn(\"can: receive list entry not found for dev %s, id %03X, mask %03X\\n\",\n \t\t\tDNAME(dev), can_id, mask);\n-\t\tgoto out;\n+\t\treturn;\n \t}\n \n \thlist_del_rcu(\u0026rcv-\u003elist);\n \tdev_rcv_lists-\u003eentries--;\n \n+\tspin_unlock_bh(\u0026dev_rcv_lists-\u003elock);\n+\n+\tspin_lock_bh(\u0026net-\u003ecan.rcvlists_lock);\n \tif (rcv_lists_stats-\u003ercv_entries \u003e 0)\n \t\trcv_lists_stats-\u003ercv_entries--;\n-\n- out:\n \tspin_unlock_bh(\u0026net-\u003ecan.rcvlists_lock);\n \n \t/* schedule the receiver item for deletion */\n-\tif (rcv) {\n-\t\tif (rcv-\u003esk)\n-\t\t\tsock_hold(rcv-\u003esk);\n-\t\tcall_rcu(\u0026rcv-\u003ercu, can_rx_delete_receiver);\n-\t}\n+\tif (rcv-\u003esk)\n+\t\tsock_hold(rcv-\u003esk);\n+\tcall_rcu(\u0026rcv-\u003ercu, can_rx_delete_receiver);\n }\n EXPORT_SYMBOL(can_rx_unregister);\n \n@@ -684,8 +694,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,\n {\n \tstruct can_skb_ext *csx = can_skb_ext_find(skb);\n \n-\tif (unlikely(dev-\u003etype != ARPHRD_CAN || !can_get_ml_priv(dev) ||\n-\t\t !csx || !can_is_can_skb(skb))) {\n+\tif (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_can_skb(skb))) {\n \t\tpr_warn_once(\"PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\\n\",\n \t\t\t dev-\u003etype, skb-\u003elen);\n \n@@ -710,8 +719,7 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,\n {\n \tstruct can_skb_ext *csx = can_skb_ext_find(skb);\n \n-\tif (unlikely(dev-\u003etype != ARPHRD_CAN || !can_get_ml_priv(dev) ||\n-\t\t !csx || !can_is_canfd_skb(skb))) {\n+\tif (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canfd_skb(skb))) {\n \t\tpr_warn_once(\"PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\\n\",\n \t\t\t dev-\u003etype, skb-\u003elen);\n \n@@ -736,8 +744,7 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,\n {\n \tstruct can_skb_ext *csx = can_skb_ext_find(skb);\n \n-\tif (unlikely(dev-\u003etype != ARPHRD_CAN || !can_get_ml_priv(dev) ||\n-\t\t !csx || !can_is_canxl_skb(skb))) {\n+\tif (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canxl_skb(skb))) {\n \t\tpr_warn_once(\"PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\\n\",\n \t\t\t dev-\u003etype, skb-\u003elen);\n \n@@ -826,6 +833,7 @@ static int can_pernet_init(struct net *net)\n \tnet-\u003ecan.rx_alldev_list = kzalloc_obj(*net-\u003ecan.rx_alldev_list);\n \tif (!net-\u003ecan.rx_alldev_list)\n \t\tgoto out;\n+\tspin_lock_init(\u0026net-\u003ecan.rx_alldev_list-\u003elock);\n \tnet-\u003ecan.pkg_stats = kzalloc_obj(*net-\u003ecan.pkg_stats);\n \tif (!net-\u003ecan.pkg_stats)\n \t\tgoto out_free_rx_alldev_list;\ndiff --git a/net/can/af_can.h b/net/can/af_can.h\nindex 87887014f5628..6e593ed5db5f0 100644\n--- a/net/can/af_can.h\n+++ b/net/can/af_can.h\n@@ -55,7 +55,7 @@ struct receiver {\n \tatomic_long_t matches;\n \tvoid (*func)(struct sk_buff *skb, void *data);\n \tvoid *data;\n-\tchar *ident;\n+\tu64 ino;\n \tstruct sock *sk;\n \tstruct rcu_head rcu;\n };\ndiff --git a/net/can/bcm.c b/net/can/bcm.c\nindex 3d637a1e0ac1a..7bd2aa39777af 100644\n--- a/net/can/bcm.c\n+++ b/net/can/bcm.c\n@@ -54,6 +54,7 @@\n #include \u003clinux/if_arp.h\u003e\n #include \u003clinux/skbuff.h\u003e\n #include \u003clinux/can.h\u003e\n+#include \u003clinux/can/can-ml.h\u003e\n #include \u003clinux/can/core.h\u003e\n #include \u003clinux/can/skb.h\u003e\n #include \u003clinux/can/bcm.h\u003e\n@@ -220,9 +221,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)\n \tstruct bcm_sock *bo = bcm_sk(sk);\n \tstruct bcm_op *op;\n \n-\tseq_printf(m, \"\u003e\u003e\u003e socket %pK\", sk-\u003esk_socket);\n-\tseq_printf(m, \" / sk %pK\", sk);\n-\tseq_printf(m, \" / bo %pK\", bo);\n+\tseq_printf(m, \"\u003e\u003e\u003e sock inode %s\", bo-\u003eprocname);\n \tseq_printf(m, \" / dropped %lu\", bo-\u003edropped_usr_msgs);\n \tseq_printf(m, \" / bound %s\", bcm_proc_getifname(net, ifname, bo-\u003eifindex));\n \tseq_printf(m, \" \u003c\u003c\u003c\\n\");\n@@ -963,7 +962,7 @@ static void bcm_remove_op(struct bcm_op *op)\n static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)\n {\n \tif (op-\u003erx_reg_dev == dev) {\n-\t\tcan_rx_unregister(dev_net(dev), dev, op-\u003ecan_id,\n+\t\tcan_rx_unregister(sock_net(op-\u003esk), dev, op-\u003ecan_id,\n \t\t\t\t REGMASK(op-\u003ecan_id), bcm_rx_handler, op);\n \n \t\t/* mark as removed subscription */\n@@ -989,21 +988,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,\n \t\t\t/* disable automatic timer on frame reception */\n \t\t\top-\u003eflags |= RX_NO_AUTOTIMER;\n \n-\t\t\t/*\n-\t\t\t * Don't care if we're bound or not (due to netdev\n-\t\t\t * problems) can_rx_unregister() is always a save\n-\t\t\t * thing to do here.\n-\t\t\t */\n \t\t\tif (op-\u003eifindex) {\n-\t\t\t\t/*\n-\t\t\t\t * Only remove subscriptions that had not\n-\t\t\t\t * been removed due to NETDEV_UNREGISTER\n-\t\t\t\t * in bcm_notifier()\n-\t\t\t\t *\n-\t\t\t\t * op-\u003erx_reg_dev is a tracked reference taken\n-\t\t\t\t * when the subscription was registered, so it\n-\t\t\t\t * stays valid here even if a concurrent\n-\t\t\t\t * NETDEV_UNREGISTER already unlisted the dev.\n+\t\t\t\t/* Only remove subscriptions that had not been\n+\t\t\t\t * removed in bcm_notify(). op-\u003erx_reg_dev keeps\n+\t\t\t\t * the device's receive lists accessible.\n \t\t\t\t */\n \t\t\t\tif (op-\u003erx_reg_dev)\n \t\t\t\t\tbcm_rx_unreg(op-\u003erx_reg_dev, op);\n@@ -1536,7 +1524,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,\n \t\t\t\t\t\t op-\u003ecan_id,\n \t\t\t\t\t\t REGMASK(op-\u003ecan_id),\n \t\t\t\t\t\t bcm_rx_handler, op,\n-\t\t\t\t\t\t \"bcm\", sk);\n+\t\t\t\t\t\t sock_i_ino(sk), sk);\n \n \t\t\t\t/* keep a tracked reference so that a later\n \t\t\t\t * unregister can safely reach the device even\n@@ -1560,7 +1548,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,\n \t\t} else {\n \t\t\terr = can_rx_register(sock_net(sk), NULL, op-\u003ecan_id,\n \t\t\t\t\t REGMASK(op-\u003ecan_id),\n-\t\t\t\t\t bcm_rx_handler, op, \"bcm\", sk);\n+\t\t\t\t\t bcm_rx_handler, op,\n+\t\t\t\t\t sock_i_ino(sk), sk);\n \t\t}\n \n \t\tif (err) {\n@@ -1719,7 +1708,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)\n \t\t\t\tgoto out_release;\n \t\t\t}\n \n-\t\t\tif (dev-\u003etype != ARPHRD_CAN) {\n+\t\t\tif (!can_get_ml_priv(dev)) {\n \t\t\t\tdev_put(dev);\n \t\t\t\tret = -ENODEV;\n \t\t\t\tgoto out_release;\n@@ -1867,7 +1856,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,\n {\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \n-\tif (dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(dev))\n \t\treturn NOTIFY_DONE;\n \tif (msg != NETDEV_UNREGISTER \u0026\u0026 msg != NETDEV_DOWN)\n \t\treturn NOTIFY_DONE;\n@@ -1935,6 +1924,7 @@ static int bcm_release(struct socket *sock)\n \tlist_del(\u0026bo-\u003enotifier);\n \tspin_unlock(\u0026bcm_notifier_lock);\n \n+\t/* bcm_notify() can not access this socket anymore */\n \tlock_sock(sk);\n \n #if IS_ENABLED(CONFIG_PROC_FS)\n@@ -1948,29 +1938,20 @@ static int bcm_release(struct socket *sock)\n \t\tbcm_remove_op(op);\n \t}\n \n-\tlist_for_each_entry_safe(op, next, \u0026bo-\u003erx_ops, list) {\n-\t\t/*\n-\t\t * Don't care if we're bound or not (due to netdev problems)\n-\t\t * can_rx_unregister() is always a save thing to do here.\n-\t\t */\n+\tlist_for_each_entry(op, \u0026bo-\u003erx_ops, list) {\n \t\tif (op-\u003eifindex) {\n-\t\t\t/*\n-\t\t\t * Only remove subscriptions that had not\n-\t\t\t * been removed due to NETDEV_UNREGISTER\n-\t\t\t * in bcm_notifier()\n-\t\t\t *\n-\t\t\t * op-\u003erx_reg_dev is a tracked reference taken\n-\t\t\t * when the subscription was registered, so it\n-\t\t\t * stays valid here even if a concurrent\n-\t\t\t * NETDEV_UNREGISTER already unlisted the device.\n+\t\t\t/* Only remove subscriptions that had not been removed\n+\t\t\t * in bcm_notify(). op-\u003erx_reg_dev keeps the device's\n+\t\t\t * receive lists accessible.\n \t\t\t */\n \t\t\tif (op-\u003erx_reg_dev)\n \t\t\t\tbcm_rx_unreg(op-\u003erx_reg_dev, op);\n-\t\t} else\n+\t\t} else {\n \t\t\tcan_rx_unregister(net, NULL, op-\u003ecan_id,\n \t\t\t\t\t REGMASK(op-\u003ecan_id),\n \t\t\t\t\t bcm_rx_handler, op);\n-\n+\t\t}\n+\t\tcond_resched();\n \t}\n \n \tsynchronize_rcu();\n@@ -2024,7 +2005,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int\n \t\t\tret = -ENODEV;\n \t\t\tgoto fail;\n \t\t}\n-\t\tif (dev-\u003etype != ARPHRD_CAN) {\n+\t\tif (!can_get_ml_priv(dev)) {\n \t\t\tdev_put(dev);\n \t\t\tret = -ENODEV;\n \t\t\tgoto fail;\n@@ -2040,8 +2021,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int\n \n #if IS_ENABLED(CONFIG_PROC_FS)\n \tif (net-\u003ecan.bcmproc_dir) {\n-\t\t/* unique socket address as filename */\n-\t\tsprintf(bo-\u003eprocname, \"%llu\", sock_i_ino(sk));\n+\t\t/* use unique socket inode number as filename */\n+\t\tsnprintf(bo-\u003eprocname, sizeof(bo-\u003eprocname),\n+\t\t\t \"%llu\", sock_i_ino(sk));\n \t\tbo-\u003ebcm_proc_read = proc_create_net_single(bo-\u003eprocname, 0644,\n \t\t\t\t\t\t net-\u003ecan.bcmproc_dir,\n \t\t\t\t\t\t bcm_proc_show, sk);\ndiff --git a/net/can/gw.c b/net/can/gw.c\nindex 5793cb2420ed3..8a5327d8a00e8 100644\n--- a/net/can/gw.c\n+++ b/net/can/gw.c\n@@ -52,6 +52,7 @@\n #include \u003clinux/if_arp.h\u003e\n #include \u003clinux/skbuff.h\u003e\n #include \u003clinux/can.h\u003e\n+#include \u003clinux/can/can-ml.h\u003e\n #include \u003clinux/can/core.h\u003e\n #include \u003clinux/can/skb.h\u003e\n #include \u003clinux/can/gw.h\u003e\n@@ -579,7 +580,7 @@ static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)\n {\n \treturn can_rx_register(net, gwj-\u003esrc.dev, gwj-\u003eccgw.filter.can_id,\n \t\t\t gwj-\u003eccgw.filter.can_mask, can_can_gw_rcv,\n-\t\t\t gwj, \"gw\", NULL);\n+\t\t\t gwj, 0, NULL);\n }\n \n static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)\n@@ -612,7 +613,7 @@ static int cgw_notifier(struct notifier_block *nb,\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \tstruct net *net = dev_net(dev);\n \n-\tif (dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(dev))\n \t\treturn NOTIFY_DONE;\n \n \tif (msg == NETDEV_UNREGISTER) {\n@@ -1163,7 +1164,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,\n \tif (!gwj-\u003esrc.dev)\n \t\tgoto out;\n \n-\tif (gwj-\u003esrc.dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(gwj-\u003esrc.dev))\n \t\tgoto out;\n \n \tgwj-\u003edst.dev = __dev_get_by_index(net, gwj-\u003eccgw.dst_idx);\n@@ -1171,7 +1172,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,\n \tif (!gwj-\u003edst.dev)\n \t\tgoto out;\n \n-\tif (gwj-\u003edst.dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(gwj-\u003edst.dev))\n \t\tgoto out;\n \n \t/* is sending the skb back to the incoming interface intended? */\ndiff --git a/net/can/isotp.c b/net/can/isotp.c\nindex f5dc9d04bd686..9386389cd702e 100644\n--- a/net/can/isotp.c\n+++ b/net/can/isotp.c\n@@ -65,6 +65,7 @@\n #include \u003clinux/if_arp.h\u003e\n #include \u003clinux/skbuff.h\u003e\n #include \u003clinux/can.h\u003e\n+#include \u003clinux/can/can-ml.h\u003e\n #include \u003clinux/can/core.h\u003e\n #include \u003clinux/can/skb.h\u003e\n #include \u003clinux/can/isotp.h\u003e\n@@ -755,6 +756,14 @@ static void isotp_rcv(struct sk_buff *skb, void *data)\n \tif (skb-\u003elen != so-\u003ell.mtu)\n \t\treturn;\n \n+\t/* check for correct CAN CC/FD frame content */\n+\tif (so-\u003ell.mtu == CAN_MTU) {\n+\t\tif (!can_is_can_skb(skb))\n+\t\t\treturn;\n+\t} else if (!can_is_canfd_skb(skb)) {\n+\t\treturn;\n+\t}\n+\n \tcf = (struct canfd_frame *)skb-\u003edata;\n \n \t/* if enabled: check reception of my configured extended address */\n@@ -1488,11 +1497,11 @@ static int isotp_release(struct socket *sock)\n \tlist_del(\u0026so-\u003enotifier);\n \tspin_unlock(\u0026isotp_notifier_lock);\n \n-\trtnl_lock();\n \tlock_sock(sk);\n \n \t/* remove current filters \u0026 unregister\n-\t * tracked reference so-\u003edev is taken at bind() time with rtnl_lock\n+\t * the tracked reference so-\u003edev keeps the device's receive lists\n+\t * accessible, even after a namespace change\n \t */\n \tif (so-\u003ebound \u0026\u0026 so-\u003edev) {\n \t\tif (isotp_register_rxid(so))\n@@ -1510,8 +1519,6 @@ static int isotp_release(struct socket *sock)\n \tso-\u003ebound = 0;\n \tso-\u003edev = NULL;\n \n-\trtnl_unlock();\n-\n \t/* Always wait for a grace period before touching the timers below.\n \t * A concurrent NETDEV_UNREGISTER may have already unregistered our\n \t * filters and cleared so-\u003ebound in isotp_notify() without waiting\n@@ -1579,7 +1586,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l\n \tif (!addr-\u003ecan_ifindex)\n \t\treturn -ENODEV;\n \n-\trtnl_lock();\n+\t/* lock_sock() serializes the binding with isotp_notify() */\n \tlock_sock(sk);\n \n \tif (so-\u003ebound) {\n@@ -1605,34 +1612,44 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l\n \t\tgoto out;\n \t}\n \n-\tdev = dev_get_by_index(net, addr-\u003ecan_ifindex);\n-\tif (!dev) {\n-\t\terr = -ENODEV;\n-\t\tgoto out;\n-\t}\n-\tif (dev-\u003etype != ARPHRD_CAN) {\n+\t/* Publish the device inside the RCU section of the lookup: When it\n+\t * is unlisted concurrently, either isotp_notify() sees so-\u003edev after\n+\t * synchronize_net() or the lookup fails. Check the device before, so\n+\t * that bind() can not fail after so-\u003edev has been published.\n+\t */\n+\trcu_read_lock();\n+\tdev = dev_get_by_index_rcu(net, addr-\u003ecan_ifindex);\n+\tif (!dev || !can_get_ml_priv(dev))\n \t\terr = -ENODEV;\n-\t\tgoto out_put_dev;\n-\t}\n-\tif (READ_ONCE(dev-\u003emtu) \u003c so-\u003ell.mtu) {\n+\telse if (READ_ONCE(dev-\u003emtu) \u003c so-\u003ell.mtu)\n \t\terr = -EINVAL;\n-\t\tgoto out_put_dev;\n+\tif (!err) {\n+\t\tdev_hold(dev);\n+\t\tWRITE_ONCE(so-\u003edev, dev);\n \t}\n-\tif (!(dev-\u003eflags \u0026 IFF_UP))\n+\trcu_read_unlock();\n+\tif (err)\n+\t\tgoto out;\n+\n+\t/* Either isotp_notify() sees so-\u003edev on NETDEV_DOWN or we see the\n+\t * cleared IFF_UP, pairs with isotp_notify()\n+\t */\n+\tsmp_mb();\n+\tif (!(READ_ONCE(dev-\u003eflags) \u0026 IFF_UP))\n \t\tnotify_enetdown = 1;\n \n \tifindex = dev-\u003eifindex;\n \n \tif (isotp_register_rxid(so))\n \t\tcan_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),\n-\t\t\t\tisotp_rcv, sk, \"isotp\", sk);\n+\t\t\t\tisotp_rcv, sk, sock_i_ino(sk), sk);\n \n \t/* no consecutive frame echo skb in flight */\n \tWRITE_ONCE(so-\u003ecfecho, 0);\n \n \t/* register for echo skb's */\n \tcan_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),\n-\t\t\tisotp_rcv_echo, sk, \"isotpe\", sk);\n+\t\t\tisotp_rcv_echo, sk, sock_i_ino(sk), sk);\n \n \t/* switch to new settings */\n \tso-\u003eifindex = ifindex;\n@@ -1644,15 +1661,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l\n \t * can safely reach the device later, even if a concurrent\n \t * NETDEV_UNREGISTER has already unlisted it by ifindex.\n \t */\n-\tso-\u003edev = dev;\n \tnetdev_hold(so-\u003edev, \u0026so-\u003edev_tracker, GFP_KERNEL);\n \n-out_put_dev:\n-\t/* remove potential reference from dev_get_by_index() */\n+\t/* remove reference from the device lookup */\n \tdev_put(dev);\n out:\n \trelease_sock(sk);\n-\trtnl_unlock();\n \n \tif (notify_enetdown) {\n \t\tsk-\u003esk_err = ENETDOWN;\n@@ -1855,12 +1869,21 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,\n \tif (!net_eq(dev_net(dev), sock_net(sk)))\n \t\treturn;\n \n-\tif (so-\u003edev != dev)\n+\tif (msg == NETDEV_DOWN)\n+\t\tsmp_mb(); /* pairs with isotp_bind(), see raw_notify() */\n+\n+\t/* lockless fast path, see raw_notify() */\n+\tif (READ_ONCE(so-\u003edev) != dev)\n \t\treturn;\n \n \tswitch (msg) {\n \tcase NETDEV_UNREGISTER:\n \t\tlock_sock(sk);\n+\t\tif (so-\u003edev != dev) {\n+\t\t\trelease_sock(sk);\n+\t\t\tbreak;\n+\t\t}\n+\n \t\t/* remove current filters \u0026 unregister */\n \t\tif (so-\u003ebound) {\n \t\t\tif (isotp_register_rxid(so))\n@@ -1876,7 +1899,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,\n \n \t\tso-\u003eifindex = 0;\n \t\tso-\u003ebound = 0;\n-\t\tso-\u003edev = NULL;\n+\t\tWRITE_ONCE(so-\u003edev, NULL);\n \t\trelease_sock(sk);\n \n \t\tsk-\u003esk_err = ENODEV;\n@@ -1897,7 +1920,7 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg,\n {\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \n-\tif (dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(dev))\n \t\treturn NOTIFY_DONE;\n \tif (msg != NETDEV_UNREGISTER \u0026\u0026 msg != NETDEV_DOWN)\n \t\treturn NOTIFY_DONE;\ndiff --git a/net/can/j1939/main.c b/net/can/j1939/main.c\nindex 5e5e6c228f225..d9384cf0e356f 100644\n--- a/net/can/j1939/main.c\n+++ b/net/can/j1939/main.c\n@@ -184,7 +184,7 @@ static int j1939_can_rx_register(struct j1939_priv *priv)\n \n \tj1939_priv_get(priv);\n \tret = can_rx_register(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,\n-\t\t\t j1939_can_recv, priv, \"j1939\", NULL);\n+\t\t\t j1939_can_recv, priv, 0, NULL);\n \tif (ret \u003c 0) {\n \t\tj1939_priv_put(priv);\n \t\treturn ret;\ndiff --git a/net/can/proc.c b/net/can/proc.c\nindex de4d05ae34597..b7496d2a37cd1 100644\n--- a/net/can/proc.c\n+++ b/net/can/proc.c\n@@ -76,16 +76,39 @@ static const char rx_list_name[][8] = {\n * af_can statistics stuff\n */\n \n+static void can_reset_pkg_stats(struct can_pkg_stats *pkg_stats)\n+{\n+\tatomic_long_set(\u0026pkg_stats-\u003erx_frames, 0);\n+\tatomic_long_set(\u0026pkg_stats-\u003etx_frames, 0);\n+\tatomic_long_set(\u0026pkg_stats-\u003ematches, 0);\n+\n+\tpkg_stats-\u003etotal_rx_rate = 0;\n+\tpkg_stats-\u003etotal_tx_rate = 0;\n+\tpkg_stats-\u003etotal_rx_match_ratio = 0;\n+\n+\tpkg_stats-\u003ecurrent_rx_rate = 0;\n+\tpkg_stats-\u003ecurrent_tx_rate = 0;\n+\tpkg_stats-\u003ecurrent_rx_match_ratio = 0;\n+\n+\tpkg_stats-\u003emax_rx_rate = 0;\n+\tpkg_stats-\u003emax_tx_rate = 0;\n+\tpkg_stats-\u003emax_rx_match_ratio = 0;\n+\n+\tatomic_long_set(\u0026pkg_stats-\u003erx_frames_delta, 0);\n+\tatomic_long_set(\u0026pkg_stats-\u003etx_frames_delta, 0);\n+\tatomic_long_set(\u0026pkg_stats-\u003ematches_delta, 0);\n+}\n+\n static void can_init_stats(struct net *net)\n {\n \tstruct can_pkg_stats *pkg_stats = net-\u003ecan.pkg_stats;\n \tstruct can_rcv_lists_stats *rcv_lists_stats = net-\u003ecan.rcv_lists_stats;\n \t/*\n-\t * This memset function is called from a timer context (when\n+\t * This stats reset is called from a timer context (when\n \t * can_stattimer is active which is the default) OR in a process\n \t * context (reading the proc_fs when can_stattimer is disabled).\n \t */\n-\tmemset(pkg_stats, 0, sizeof(struct can_pkg_stats));\n+\tcan_reset_pkg_stats(pkg_stats);\n \tpkg_stats-\u003ejiffies_init = jiffies;\n \n \trcv_lists_stats-\u003estats_reset++;\n@@ -121,29 +144,23 @@ void can_stat_update(struct timer_list *t)\n \tlong rx_frames = atomic_long_read(\u0026pkg_stats-\u003erx_frames);\n \tlong tx_frames = atomic_long_read(\u0026pkg_stats-\u003etx_frames);\n \tlong matches = atomic_long_read(\u0026pkg_stats-\u003ematches);\n-\tlong rx_frames_delta = atomic_long_read(\u0026pkg_stats-\u003erx_frames_delta);\n-\tlong tx_frames_delta = atomic_long_read(\u0026pkg_stats-\u003etx_frames_delta);\n-\tlong matches_delta = atomic_long_read(\u0026pkg_stats-\u003ematches_delta);\n+\t/* fetch and clear values for 'current rate' calculation */\n+\tlong rx_frames_delta = atomic_long_xchg(\u0026pkg_stats-\u003erx_frames_delta, 0);\n+\tlong tx_frames_delta = atomic_long_xchg(\u0026pkg_stats-\u003etx_frames_delta, 0);\n+\tlong matches_delta = atomic_long_xchg(\u0026pkg_stats-\u003ematches_delta, 0);\n \n-\t/* restart counting in timer context on user request */\n-\tif (user_reset)\n-\t\tcan_init_stats(net);\n-\n-\t/* restart counting on jiffies overflow */\n-\tif (j \u003c pkg_stats-\u003ejiffies_init)\n-\t\tcan_init_stats(net);\n-\n-\t/* prevent overflow in calc_rate() */\n-\tif (rx_frames \u003e (LONG_MAX / HZ))\n-\t\tcan_init_stats(net);\n-\n-\t/* prevent overflow in calc_rate() */\n-\tif (tx_frames \u003e (LONG_MAX / HZ))\n-\t\tcan_init_stats(net);\n-\n-\t/* matches overflow - very improbable */\n-\tif (matches \u003e (LONG_MAX / 100))\n+\t/*\n+\t * Restart counting in timer context on user request, on jiffies\n+\t * overflow or to prevent overflows in calc_rate() and in the\n+\t * match ratio calculation. The local snapshots are stale after\n+\t * the reset, so skip the calculation for this interval.\n+\t */\n+\tif (user_reset || j \u003c pkg_stats-\u003ejiffies_init ||\n+\t rx_frames \u003e (LONG_MAX / HZ) || tx_frames \u003e (LONG_MAX / HZ) ||\n+\t matches \u003e (LONG_MAX / 100)) {\n \t\tcan_init_stats(net);\n+\t\tgoto out;\n+\t}\n \n \t/* calc total values */\n \tif (rx_frames)\n@@ -172,11 +189,7 @@ void can_stat_update(struct timer_list *t)\n \tif (pkg_stats-\u003emax_rx_match_ratio \u003c pkg_stats-\u003ecurrent_rx_match_ratio)\n \t\tpkg_stats-\u003emax_rx_match_ratio = pkg_stats-\u003ecurrent_rx_match_ratio;\n \n-\t/* clear values for 'current rate' calculation */\n-\tatomic_long_set(\u0026pkg_stats-\u003etx_frames_delta, 0);\n-\tatomic_long_set(\u0026pkg_stats-\u003erx_frames_delta, 0);\n-\tatomic_long_set(\u0026pkg_stats-\u003ematches_delta, 0);\n-\n+out:\n \t/* restart timer (one second) */\n \tmod_timer(\u0026net-\u003ecan.stattimer, round_jiffies(jiffies + HZ));\n }\n@@ -191,26 +204,32 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,\n \tstruct receiver *r;\n \n \thlist_for_each_entry_rcu(r, rx_list, list) {\n+\t\tchar symcheck[4];\n \t\tchar *fmt = (r-\u003ecan_id \u0026 CAN_EFF_FLAG)?\n-\t\t\t\" %-5s %08x %08x %pK %pK %8ld %s\\n\" :\n-\t\t\t\" %-5s %03x %08x %pK %pK %8ld %s\\n\";\n+\t\t\t\" %6s %08x %08x %8ld %10llu\" :\n+\t\t\t\" %6s %03x %08x %8ld %10llu\";\n \n \t\tseq_printf(m, fmt, DNAME(dev), r-\u003ecan_id, r-\u003emask,\n-\t\t\t r-\u003efunc, r-\u003edata, atomic_long_read(\u0026r-\u003ematches),\n-\t\t\t r-\u003eident);\n+\t\t\t atomic_long_read(\u0026r-\u003ematches), r-\u003eino);\n+\n+\t\t/* %ps falls back to the raw text address when the symbol\n+\t\t * can not be resolved (KALLSYMS=n, stripped module symbols)\n+\t\t */\n+\t\tsnprintf(symcheck, sizeof(symcheck), \"%ps\", r-\u003efunc);\n+\t\tif (strncmp(symcheck, \"0x\", 2))\n+\t\t\tseq_printf(m, \" %ps\\n\", r-\u003efunc);\n+\t\telse\n+\t\t\tseq_puts(m, \" (unknown)\\n\");\n \t}\n }\n \n static void can_print_recv_banner(struct seq_file *m)\n {\n \t/*\n-\t * can1. 00000000 00000000 00000000\n-\t * ....... 0 tp20\n+\t * device can_id can_mask matches sock_inode function\n+\t * vcan0 000 00000000 0 43798 raw_rcv [can_raw]\n \t */\n-\tif (IS_ENABLED(CONFIG_64BIT))\n-\t\tseq_puts(m, \" device can_id can_mask function userdata matches ident\\n\");\n-\telse\n-\t\tseq_puts(m, \" device can_id can_mask function userdata matches ident\\n\");\n+\tseq_puts(m, \" device can_id can_mask matches sock_inode function\\n\");\n }\n \n static int can_stats_proc_show(struct seq_file *m, void *v)\ndiff --git a/net/can/raw.c b/net/can/raw.c\nindex 0acd4f6c6dd67..d07f3bb68cd5c 100644\n--- a/net/can/raw.c\n+++ b/net/can/raw.c\n@@ -85,6 +85,7 @@ struct raw_sock {\n \tstruct sock sk;\n \tstruct net_device *dev;\n \tnetdevice_tracker dev_tracker;\n+\tstruct net_device *bind_dev; /* hint for raw_notify(), no reference */\n \tstruct list_head notifier;\n \tint ifindex;\n \tunsigned int bound:1;\n@@ -224,9 +225,10 @@ static int raw_enable_filters(struct net *net, struct net_device *dev,\n \tint i;\n \n \tfor (i = 0; i \u003c count; i++) {\n+\t\tcond_resched();\n \t\terr = can_rx_register(net, dev, filter[i].can_id,\n \t\t\t\t filter[i].can_mask,\n-\t\t\t\t raw_rcv, sk, \"raw\", sk);\n+\t\t\t\t raw_rcv, sk, sock_i_ino(sk), sk);\n \t\tif (err) {\n \t\t\t/* clean up successfully registered filters */\n \t\t\twhile (--i \u003e= 0)\n@@ -247,7 +249,7 @@ static int raw_enable_errfilter(struct net *net, struct net_device *dev,\n \n \tif (err_mask)\n \t\terr = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,\n-\t\t\t\t raw_rcv, sk, \"raw\", sk);\n+\t\t\t\t raw_rcv, sk, sock_i_ino(sk), sk);\n \n \treturn err;\n }\n@@ -258,9 +260,11 @@ static void raw_disable_filters(struct net *net, struct net_device *dev,\n {\n \tint i;\n \n-\tfor (i = 0; i \u003c count; i++)\n+\tfor (i = 0; i \u003c count; i++) {\n \t\tcan_rx_unregister(net, dev, filter[i].can_id,\n \t\t\t\t filter[i].can_mask, raw_rcv, sk);\n+\t\tcond_resched();\n+\t}\n }\n \n static inline void raw_disable_errfilter(struct net *net,\n@@ -305,16 +309,31 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,\n \t\t struct net_device *dev)\n {\n \tstruct sock *sk = \u0026ro-\u003esk;\n+\tstruct net_device *bind_dev;\n \n \tif (!net_eq(dev_net(dev), sock_net(sk)))\n \t\treturn;\n \n-\tif (ro-\u003edev != dev)\n+\tif (msg == NETDEV_DOWN)\n+\t\tsmp_mb(); /* IFF_UP is cleared before, pairs with raw_bind() */\n+\n+\t/* Lockless fast path, ro-\u003ebind_dev covers a concurrent bind().\n+\t * Read it before ro-\u003edev, pairs with smp_store_release() in\n+\t * raw_bind(). A stale ro-\u003ebind_dev only costs a lock_sock().\n+\t */\n+\tbind_dev = smp_load_acquire(\u0026ro-\u003ebind_dev);\n+\tif (READ_ONCE(ro-\u003edev) != dev \u0026\u0026\n+\t (msg != NETDEV_UNREGISTER || bind_dev != dev))\n \t\treturn;\n \n \tswitch (msg) {\n \tcase NETDEV_UNREGISTER:\n \t\tlock_sock(sk);\n+\t\tif (ro-\u003edev != dev) {\n+\t\t\trelease_sock(sk);\n+\t\t\tbreak;\n+\t\t}\n+\n \t\t/* remove current filters \u0026 unregister */\n \t\tif (ro-\u003ebound) {\n \t\t\traw_disable_allfilters(dev_net(dev), dev, sk);\n@@ -326,7 +345,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,\n \n \t\tro-\u003eifindex = 0;\n \t\tro-\u003ebound = 0;\n-\t\tro-\u003edev = NULL;\n+\t\tWRITE_ONCE(ro-\u003edev, NULL);\n \t\tro-\u003ecount = 0;\n \t\trelease_sock(sk);\n \n@@ -348,7 +367,7 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,\n {\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \n-\tif (dev-\u003etype != ARPHRD_CAN)\n+\tif (!can_get_ml_priv(dev))\n \t\treturn NOTIFY_DONE;\n \tif (msg != NETDEV_UNREGISTER \u0026\u0026 msg != NETDEV_DOWN)\n \t\treturn NOTIFY_DONE;\n@@ -381,6 +400,7 @@ static int raw_init(struct sock *sk)\n \tro-\u003ebound = 0;\n \tro-\u003eifindex = 0;\n \tro-\u003edev = NULL;\n+\tro-\u003ebind_dev = NULL;\n \n \t/* set default filter to single entry dfilter */\n \tro-\u003edfilter.can_id = 0;\n@@ -431,13 +451,15 @@ static int raw_release(struct socket *sock)\n \tlist_del(\u0026ro-\u003enotifier);\n \tspin_unlock(\u0026raw_notifier_lock);\n \n-\trtnl_lock();\n \tlock_sock(sk);\n \n-\t/* remove current filters \u0026 unregister */\n+\t/* remove current filters \u0026 unregister\n+\t * the tracked reference ro-\u003edev keeps the device's receive lists\n+\t * accessible, even after a namespace change\n+\t */\n \tif (ro-\u003ebound) {\n \t\tif (ro-\u003edev) {\n-\t\t\traw_disable_allfilters(dev_net(ro-\u003edev), ro-\u003edev, sk);\n+\t\t\traw_disable_allfilters(net, ro-\u003edev, sk);\n \t\t\tnetdev_put(ro-\u003edev, \u0026ro-\u003edev_tracker);\n \t\t} else {\n \t\t\traw_disable_allfilters(net, NULL, sk);\n@@ -456,7 +478,6 @@ static int raw_release(struct socket *sock)\n \tsock-\u003esk = NULL;\n \n \trelease_sock(sk);\n-\trtnl_unlock();\n \n \tsock_prot_inuse_add(net, sk-\u003esk_prot, -1);\n \tsock_put(sk);\n@@ -479,26 +500,34 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len\n \tif (addr-\u003ecan_family != AF_CAN)\n \t\treturn -EINVAL;\n \n-\trtnl_lock();\n+\t/* lock_sock() serializes the binding with raw_notify() */\n \tlock_sock(sk);\n \n \tif (ro-\u003ebound \u0026\u0026 addr-\u003ecan_ifindex == ro-\u003eifindex)\n \t\tgoto out;\n \n \tif (addr-\u003ecan_ifindex) {\n-\t\tdev = dev_get_by_index(sock_net(sk), addr-\u003ecan_ifindex);\n+\t\t/* Publish the device inside the RCU section of the lookup:\n+\t\t * When it is unlisted concurrently, either raw_notify() sees\n+\t\t * ro-\u003ebind_dev after synchronize_net() or the lookup fails.\n+\t\t */\n+\t\trcu_read_lock();\n+\t\tdev = dev_get_by_index_rcu(sock_net(sk), addr-\u003ecan_ifindex);\n+\t\tif (dev) {\n+\t\t\tdev_hold(dev);\n+\t\t\t/* pairs with smp_load_acquire() in raw_notify() */\n+\t\t\tsmp_store_release(\u0026ro-\u003ebind_dev, dev);\n+\t\t}\n+\t\trcu_read_unlock();\n \t\tif (!dev) {\n \t\t\terr = -ENODEV;\n \t\t\tgoto out;\n \t\t}\n-\t\tif (dev-\u003etype != ARPHRD_CAN) {\n+\t\tif (!can_get_ml_priv(dev)) {\n \t\t\terr = -ENODEV;\n \t\t\tgoto out_put_dev;\n \t\t}\n \n-\t\tif (!(dev-\u003eflags \u0026 IFF_UP))\n-\t\t\tnotify_enetdown = 1;\n-\n \t\tifindex = dev-\u003eifindex;\n \n \t\t/* filters set by default/setsockopt */\n@@ -517,7 +546,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len\n \t\tif (ro-\u003ebound) {\n \t\t\t/* unregister old filters */\n \t\t\tif (ro-\u003edev) {\n-\t\t\t\traw_disable_allfilters(dev_net(ro-\u003edev),\n+\t\t\t\traw_disable_allfilters(sock_net(sk),\n \t\t\t\t\t\t ro-\u003edev, sk);\n \t\t\t\t/* drop reference to old ro-\u003edev */\n \t\t\t\tnetdev_put(ro-\u003edev, \u0026ro-\u003edev_tracker);\n@@ -528,17 +557,24 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len\n \t\tro-\u003eifindex = ifindex;\n \t\tro-\u003ebound = 1;\n \t\t/* bind() ok -\u003e hold a reference for new ro-\u003edev */\n-\t\tro-\u003edev = dev;\n-\t\tif (ro-\u003edev)\n-\t\t\tnetdev_hold(ro-\u003edev, \u0026ro-\u003edev_tracker, GFP_KERNEL);\n+\t\tWRITE_ONCE(ro-\u003edev, dev);\n+\t\tnetdev_hold(ro-\u003edev, \u0026ro-\u003edev_tracker, GFP_KERNEL);\n+\n+\t\tif (dev) {\n+\t\t\t/* Either raw_notify() sees ro-\u003edev on NETDEV_DOWN or\n+\t\t\t * we see the cleared IFF_UP, pairs with raw_notify()\n+\t\t\t */\n+\t\t\tsmp_mb();\n+\t\t\tif (!(READ_ONCE(dev-\u003eflags) \u0026 IFF_UP))\n+\t\t\t\tnotify_enetdown = 1;\n+\t\t}\n \t}\n \n out_put_dev:\n-\t/* remove potential reference from dev_get_by_index() */\n+\t/* remove reference from the device lookup */\n \tdev_put(dev);\n out:\n \trelease_sock(sk);\n-\trtnl_unlock();\n \n \tif (notify_enetdown) {\n \t\tsk-\u003esk_err = ENETDOWN;\n@@ -567,58 +603,41 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,\n }\n \n static int raw_setsockopt_locked(struct socket *sock, int optname,\n-\t\t\t\t sockptr_t optval, unsigned int optlen)\n+\t\t\t\t sockptr_t optval, unsigned int optlen,\n+\t\t\t\t struct can_filter *filter,\n+\t\t\t\t struct can_filter *sfilter, int count)\n {\n \tstruct sock *sk = sock-\u003esk;\n \tstruct raw_sock *ro = raw_sk(sk);\n-\tstruct can_filter *filter = NULL; /* dyn. alloc'ed filters */\n-\tstruct can_filter sfilter; /* single filter */\n \tstruct net_device *dev = NULL;\n \tcan_err_mask_t err_mask = 0;\n-\tint count = 0;\n \tint flag;\n \tint err = 0;\n \n \tswitch (optname) {\n \tcase CAN_RAW_FILTER:\n-\t\tif (optlen % sizeof(struct can_filter) != 0)\n-\t\t\treturn -EINVAL;\n-\n-\t\tif (optlen \u003e CAN_RAW_FILTER_MAX * sizeof(struct can_filter))\n-\t\t\treturn -EINVAL;\n-\n-\t\tcount = optlen / sizeof(struct can_filter);\n-\n-\t\tif (count \u003e 1) {\n-\t\t\t/* filter does not fit into dfilter =\u003e alloc space */\n-\t\t\tfilter = memdup_sockptr(optval, optlen);\n-\t\t\tif (IS_ERR(filter))\n-\t\t\t\treturn PTR_ERR(filter);\n-\t\t} else if (count == 1) {\n-\t\t\tif (copy_from_sockptr(\u0026sfilter, optval, sizeof(sfilter)))\n-\t\t\t\treturn -EFAULT;\n-\t\t}\n-\n+\t\t/* @filter has been copied by raw_setsockopt(). It is linked\n+\t\t * to the socket on success and freed by the caller on error.\n+\t\t *\n+\t\t * The reg_state check is only a shortcut: On a concurrent\n+\t\t * unregistration or namespace change raw_notify() waits for\n+\t\t * lock_sock() and removes the filters set here.\n+\t\t */\n \t\tdev = ro-\u003edev;\n-\t\tif (ro-\u003ebound \u0026\u0026 dev \u0026\u0026 dev-\u003ereg_state != NETREG_REGISTERED) {\n-\t\t\tif (count \u003e 1)\n-\t\t\t\tkfree(filter);\n+\t\tif (ro-\u003ebound \u0026\u0026 dev \u0026\u0026\n+\t\t READ_ONCE(dev-\u003ereg_state) != NETREG_REGISTERED)\n \t\t\treturn -ENODEV;\n-\t\t}\n \n \t\tif (ro-\u003ebound) {\n \t\t\t/* (try to) register the new filters */\n \t\t\tif (count == 1)\n \t\t\t\terr = raw_enable_filters(sock_net(sk), dev, sk,\n-\t\t\t\t\t\t\t \u0026sfilter, 1);\n+\t\t\t\t\t\t\t sfilter, 1);\n \t\t\telse\n \t\t\t\terr = raw_enable_filters(sock_net(sk), dev, sk,\n \t\t\t\t\t\t\t filter, count);\n-\t\t\tif (err) {\n-\t\t\t\tif (count \u003e 1)\n-\t\t\t\t\tkfree(filter);\n+\t\t\tif (err)\n \t\t\t\treturn err;\n-\t\t\t}\n \n \t\t\t/* remove old filter registrations */\n \t\t\traw_disable_filters(sock_net(sk), dev, sk, ro-\u003efilter,\n@@ -632,7 +651,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,\n \t\t/* link new filters to the socket */\n \t\tif (count == 1) {\n \t\t\t/* copy filter data for single filter */\n-\t\t\tro-\u003edfilter = sfilter;\n+\t\t\tro-\u003edfilter = *sfilter;\n \t\t\tfilter = \u0026ro-\u003edfilter;\n \t\t}\n \t\tro-\u003efilter = filter;\n@@ -648,8 +667,10 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,\n \n \t\terr_mask \u0026= CAN_ERR_MASK;\n \n+\t\t/* lockless shortcut, see CAN_RAW_FILTER */\n \t\tdev = ro-\u003edev;\n-\t\tif (ro-\u003ebound \u0026\u0026 dev \u0026\u0026 dev-\u003ereg_state != NETREG_REGISTERED)\n+\t\tif (ro-\u003ebound \u0026\u0026 dev \u0026\u0026\n+\t\t READ_ONCE(dev-\u003ereg_state) != NETREG_REGISTERED)\n \t\t\treturn -ENODEV;\n \n \t\t/* remove current error mask */\n@@ -751,18 +772,47 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,\n \t\t\t sockptr_t optval, unsigned int optlen)\n {\n \tstruct sock *sk = sock-\u003esk;\n+\tstruct can_filter *filter = NULL; /* dyn. alloc'ed filters */\n+\tstruct can_filter sfilter; /* single filter */\n+\tint count = 0;\n \tint err;\n \n \tif (level != SOL_CAN_RAW)\n \t\treturn -EINVAL;\n \n-\trtnl_lock();\n+\tif (optname == CAN_RAW_FILTER) {\n+\t\t/* Copy the filters before lock_sock(), so a stalled copy can\n+\t\t * not block raw_notify() waiting for lock_sock() under RTNL.\n+\t\t */\n+\t\tif (optlen % sizeof(struct can_filter) != 0)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (optlen \u003e CAN_RAW_FILTER_MAX * sizeof(struct can_filter))\n+\t\t\treturn -EINVAL;\n+\n+\t\tcount = optlen / sizeof(struct can_filter);\n+\n+\t\tif (count \u003e 1) {\n+\t\t\t/* filter does not fit into dfilter =\u003e alloc space */\n+\t\t\tfilter = memdup_sockptr(optval, optlen);\n+\t\t\tif (IS_ERR(filter))\n+\t\t\t\treturn PTR_ERR(filter);\n+\t\t} else if (count == 1) {\n+\t\t\tif (copy_from_sockptr(\u0026sfilter, optval, sizeof(sfilter)))\n+\t\t\t\treturn -EFAULT;\n+\t\t}\n+\t}\n+\n \tlock_sock(sk);\n \n-\terr = raw_setsockopt_locked(sock, optname, optval, optlen);\n+\terr = raw_setsockopt_locked(sock, optname, optval, optlen,\n+\t\t\t\t filter, \u0026sfilter, count);\n \n \trelease_sock(sk);\n-\trtnl_unlock();\n+\n+\t/* on error free the filters allocated for CAN_RAW_FILTER above */\n+\tif (err \u0026\u0026 count \u003e 1)\n+\t\tkfree(filter);\n \n \treturn err;\n }\n"
} |
| 2/1 | 2026/10/22 11:37 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"CAN",
"CAN_RAW",
"CAN_BCM",
"CAN_ISOTP",
"CAN_GW",
"CAN_J1939",
"CAN_DEV",
"CAN_VCAN",
"CAN_VXCAN"
],
"FocusSymbols": [
"can_rx_register",
"can_rx_unregister",
"raw_bind",
"raw_release",
"raw_setsockopt",
"raw_notify",
"isotp_bind",
"isotp_release",
"isotp_notify",
"isotp_rcv",
"bcm_release",
"bcm_rx_unreg",
"can_dropped_invalid_skb",
"can_put_echo_skb"
],
"Reasoning": "The patch modifies the Linux CAN subsystem (af_can, can-raw, can-isotp, can-bcm, and can-dev). It introduces significant synchronization and locking refactorings: removing rtnl_lock from raw and isotp socket bind, release, and setsockopt; switching from a global rcvlists_lock to per-device spinlocks; introducing lockless RCU device lookups and memory barriers (smp_store_release, smp_load_acquire, smp_mb) for netdev notifier events; and handling cross-namespace device unregistration. These reachable paths are accessible via AF_CAN sockets and virtual CAN devices (vcan, vxcan), making the changes highly susceptible to race conditions, memory ordering errors, and deadlocks that warrant fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit b71beff35437da5a2578cb3ac13b10a616a1f59f
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 22 11:37:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/can.rst b/Documentation/networking/can.rst
index 536ff411da1d1..51bc70748f753 100644
--- a/Documentation/networking/can.rst
+++ b/Documentation/networking/can.rst
@@ -1040,17 +1040,21 @@ As described in :ref:`socketcan-receive-lists` the SocketCAN core uses several f
lists to deliver received CAN frames to CAN protocol modules. These
receive lists, their filters and the count of filter matches can be
checked in the appropriate receive list. All entries contain the
-device and a protocol module identifier::
+CAN network device, the filter (can_id/can_mask), the number of filter
+matches, the inode number of the subscribing socket and the name of the
+receive function. A sock_inode of 0 denotes a kernel internal subscriber
+without a socket (e.g. CAN gateway or J1939). When the function name can
+not be resolved (e.g. with CONFIG_KALLSYMS=n) "(unknown)" is printed::
foo@bar:~$ cat /proc/net/can/rcvlist_all
receive list 'rx_all':
- (vcan3: no entry)
- (vcan2: no entry)
- (vcan1: no entry)
- device can_id can_mask function userdata matches ident
- vcan0 000 00000000 f88e6370 f6c6f400 0 raw
(any: no entry)
+ device can_id can_mask matches sock_inode function
+ vcan0 000 00000000 0 63586 raw_rcv [can_raw]
+ (vcan1: no entry)
+ (vcan2: no entry)
+ (vcan3: no entry)
In this example an application requests any CAN traffic from vcan0::
diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
index 14edec5afb57e..738d5968dfa68 100644
--- a/drivers/net/can/dev/skb.c
+++ b/drivers/net/can/dev/skb.c
@@ -4,6 +4,7 @@
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
*/
+#include <linux/can/can-ml.h>
#include <linux/can/dev.h>
#include <linux/module.h>
#include <net/can.h>
@@ -54,6 +55,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
if (idx >= priv->echo_skb_max) {
netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
__func__, idx, priv->echo_skb_max);
+ dev_kfree_skb_any(skb);
return -EINVAL;
}
@@ -62,7 +64,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
(skb->protocol != htons(ETH_P_CAN) &&
skb->protocol != htons(ETH_P_CANFD) &&
skb->protocol != htons(ETH_P_CANXL))) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return 0;
}
@@ -90,7 +92,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
} else {
/* locking problem with netif_stop_queue() ?? */
netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return -EBUSY;
}
@@ -227,7 +229,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_cc;
}
@@ -258,7 +260,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_fd;
}
@@ -296,7 +298,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_xl;
}
@@ -388,7 +390,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
break;
case ETH_P_CANXL:
- if (!can_is_canxl_skb(skb))
+ if (!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb))
goto inval_skb;
break;
@@ -402,7 +404,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
return false;
inval_skb:
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
return true;
}
diff --git a/include/linux/can/can-ml.h b/include/linux/can/can-ml.h
index 1e99fda2b3803..cfefa4a385be9 100644
--- a/include/linux/can/can-ml.h
+++ b/include/linux/can/can-ml.h
@@ -45,6 +45,7 @@
#include <linux/can.h>
#include <linux/list.h>
#include <linux/netdevice.h>
+#include <linux/spinlock.h>
/* exposed CAN device capabilities for network layer */
#define CAN_CAP_CC BIT(0) /* CAN CC aka Classical CAN */
@@ -59,6 +60,7 @@
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
struct can_dev_rcv_lists {
+ spinlock_t lock; /* protects the receive lists and entries */
struct hlist_head rx[RX_MAX];
struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
@@ -81,6 +83,7 @@ static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)
static inline void can_set_ml_priv(struct net_device *dev,
struct can_ml_priv *ml_priv)
{
+ spin_lock_init(&ml_priv->dev_rcv_lists.lock);
netdev_set_ml_priv(dev, ml_priv, ML_PRIV_CAN);
}
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 2de74c2b78b6c..effb5c31ef401 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -51,7 +51,7 @@ extern void can_proto_unregister(const struct can_proto *cp);
int can_rx_register(struct net *net, struct net_device *dev,
canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *),
- void *data, char *ident, struct sock *sk);
+ void *data, u64 ino, struct sock *sk);
extern void can_rx_unregister(struct net *net, struct net_device *dev,
canid_t can_id, canid_t mask,
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 6d0710d6f571d..346faddee02eb 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -176,7 +176,7 @@ static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *s
return can_dropped_invalid_skb(dev, skb);
invalid_skb:
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
return true;
}
diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h
index 5d27843862fca..87ace09c0d8bd 100644
--- a/include/linux/can/skb.h
+++ b/include/linux/can/skb.h
@@ -78,12 +78,12 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
nskb = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!nskb)) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NULL;
}
can_skb_set_owner(nskb, skb->sk);
- consume_skb(skb);
+ dev_consume_skb_any(skb);
return nskb;
}
diff --git a/include/net/netns/can.h b/include/net/netns/can.h
index 48b79f7e6236d..54f23681e0d45 100644
--- a/include/net/netns/can.h
+++ b/include/net/netns/can.h
@@ -29,7 +29,7 @@ struct netns_can {
/* receive filters subscribed for 'all' CAN devices */
struct can_dev_rcv_lists *rx_alldev_list;
- spinlock_t rcvlists_lock;
+ spinlock_t rcvlists_lock; /* protects the rcv_entries(_max) stats */
struct timer_list stattimer; /* timer for statistics update */
struct can_pkg_stats *pkg_stats;
struct can_rcv_lists_stats *rcv_lists_stats;
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 34fe3b28d576f..0f3051a61aab5 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -226,7 +226,7 @@ int can_send(struct sk_buff *skb, int loop)
goto inval_skb;
}
- if (unlikely(skb->dev->type != ARPHRD_CAN)) {
+ if (unlikely(!can_get_ml_priv(skb->dev))) {
err = -EPERM;
goto inval_skb;
}
@@ -418,7 +418,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
* @mask: CAN mask (see description)
* @func: callback function on filter match
* @data: returned parameter for callback function
- * @ident: string for calling module identification
+ * @ino: inode number of sock (0 = unknown)
* @sk: socket pointer (might be NULL)
*
* Description:
@@ -443,7 +443,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
*/
int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
canid_t mask, void (*func)(struct sk_buff *, void *),
- void *data, char *ident, struct sock *sk)
+ void *data, u64 ino, struct sock *sk)
{
struct receiver *rcv;
struct hlist_head *rcv_list;
@@ -452,7 +452,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
/* insert new receiver (dev,canid,mask) -> (func,data) */
- if (dev && (dev->type != ARPHRD_CAN || !can_get_ml_priv(dev)))
+ if (dev && !can_get_ml_priv(dev))
return -ENODEV;
if (dev && !net_eq(net, dev_net(dev)))
@@ -462,9 +462,10 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
if (!rcv)
return -ENOMEM;
- spin_lock_bh(&net->can.rcvlists_lock);
-
dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
+
+ spin_lock_bh(&dev_rcv_lists->lock);
+
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
rcv->can_id = can_id;
@@ -472,12 +473,15 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
atomic_long_set(&rcv->matches, 0);
rcv->func = func;
rcv->data = data;
- rcv->ident = ident;
+ rcv->ino = ino;
rcv->sk = sk;
hlist_add_head_rcu(&rcv->list, rcv_list);
dev_rcv_lists->entries++;
+ spin_unlock_bh(&dev_rcv_lists->lock);
+
+ spin_lock_bh(&net->can.rcvlists_lock);
rcv_lists_stats->rcv_entries++;
rcv_lists_stats->rcv_entries_max = max(rcv_lists_stats->rcv_entries_max,
rcv_lists_stats->rcv_entries);
@@ -509,6 +513,14 @@ static void can_rx_delete_receiver(struct rcu_head *rp)
*
* Description:
* Removes subscription entry depending on given (subscription) values.
+ * The receive lists of a CAN device are protected by a per-device lock,
+ * so the subscription is found even when the device has been moved to
+ * another network namespace after can_rx_register(). The caller has to
+ * ensure that @dev stays valid, e.g. by holding a reference to @dev or
+ * RTNL. @net should be the namespace that has been used for
+ * can_rx_register(). It selects the 'any' CAN device lists for
+ * @dev == NULL and is otherwise only used for the receive list
+ * statistics, which become imprecise when another namespace is passed.
*/
void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
canid_t mask, void (*func)(struct sk_buff *, void *),
@@ -519,15 +531,13 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
struct can_dev_rcv_lists *dev_rcv_lists;
- if (dev && dev->type != ARPHRD_CAN)
+ if (dev && !can_get_ml_priv(dev))
return;
- if (dev && !net_eq(net, dev_net(dev)))
- return;
+ dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
- spin_lock_bh(&net->can.rcvlists_lock);
+ spin_lock_bh(&dev_rcv_lists->lock);
- dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
/* Search the receiver list for the item to delete. This should
@@ -547,26 +557,26 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
* a warning here.
*/
if (!rcv) {
+ spin_unlock_bh(&dev_rcv_lists->lock);
pr_warn("can: receive list entry not found for dev %s, id %03X, mask %03X\n",
DNAME(dev), can_id, mask);
- goto out;
+ return;
}
hlist_del_rcu(&rcv->list);
dev_rcv_lists->entries--;
+ spin_unlock_bh(&dev_rcv_lists->lock);
+
+ spin_lock_bh(&net->can.rcvlists_lock);
if (rcv_lists_stats->rcv_entries > 0)
rcv_lists_stats->rcv_entries--;
-
- out:
spin_unlock_bh(&net->can.rcvlists_lock);
/* schedule the receiver item for deletion */
- if (rcv) {
- if (rcv->sk)
- sock_hold(rcv->sk);
- call_rcu(&rcv->rcu, can_rx_delete_receiver);
- }
+ if (rcv->sk)
+ sock_hold(rcv->sk);
+ call_rcu(&rcv->rcu, can_rx_delete_receiver);
}
EXPORT_SYMBOL(can_rx_unregister);
@@ -684,8 +694,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_can_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -710,8 +719,7 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_canfd_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -736,8 +744,7 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_canxl_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -826,6 +833,7 @@ static int can_pernet_init(struct net *net)
net->can.rx_alldev_list = kzalloc_obj(*net->can.rx_alldev_list);
if (!net->can.rx_alldev_list)
goto out;
+ spin_lock_init(&net->can.rx_alldev_list->lock);
net->can.pkg_stats = kzalloc_obj(*net->can.pkg_stats);
if (!net->can.pkg_stats)
goto out_free_rx_alldev_list;
diff --git a/net/can/af_can.h b/net/can/af_can.h
index 87887014f5628..6e593ed5db5f0 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -55,7 +55,7 @@ struct receiver {
atomic_long_t matches;
void (*func)(struct sk_buff *skb, void *data);
void *data;
- char *ident;
+ u64 ino;
struct sock *sk;
struct rcu_head rcu;
};
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3d637a1e0ac1a..7bd2aa39777af 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -54,6 +54,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
@@ -220,9 +221,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
- seq_printf(m, ">>> socket %pK", sk->sk_socket);
- seq_printf(m, " / sk %pK", sk);
- seq_printf(m, " / bo %pK", bo);
+ seq_printf(m, ">>> sock inode %s", bo->procname);
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
seq_printf(m, " <<<\n");
@@ -963,7 +962,7 @@ static void bcm_remove_op(struct bcm_op *op)
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{
if (op->rx_reg_dev == dev) {
- can_rx_unregister(dev_net(dev), dev, op->can_id,
+ can_rx_unregister(sock_net(op->sk), dev, op->can_id,
REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */
@@ -989,21 +988,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
/* disable automatic timer on frame reception */
op->flags |= RX_NO_AUTOTIMER;
- /*
- * Don't care if we're bound or not (due to netdev
- * problems) can_rx_unregister() is always a save
- * thing to do here.
- */
if (op->ifindex) {
- /*
- * Only remove subscriptions that had not
- * been removed due to NETDEV_UNREGISTER
- * in bcm_notifier()
- *
- * op->rx_reg_dev is a tracked reference taken
- * when the subscription was registered, so it
- * stays valid here even if a concurrent
- * NETDEV_UNREGISTER already unlisted the dev.
+ /* Only remove subscriptions that had not been
+ * removed in bcm_notify(). op->rx_reg_dev keeps
+ * the device's receive lists accessible.
*/
if (op->rx_reg_dev)
bcm_rx_unreg(op->rx_reg_dev, op);
@@ -1536,7 +1524,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
- "bcm", sk);
+ sock_i_ino(sk), sk);
/* keep a tracked reference so that a later
* unregister can safely reach the device even
@@ -1560,7 +1548,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
} else {
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
- bcm_rx_handler, op, "bcm", sk);
+ bcm_rx_handler, op,
+ sock_i_ino(sk), sk);
}
if (err) {
@@ -1719,7 +1708,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
goto out_release;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
dev_put(dev);
ret = -ENODEV;
goto out_release;
@@ -1867,7 +1856,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
@@ -1935,6 +1924,7 @@ static int bcm_release(struct socket *sock)
list_del(&bo->notifier);
spin_unlock(&bcm_notifier_lock);
+ /* bcm_notify() can not access this socket anymore */
lock_sock(sk);
#if IS_ENABLED(CONFIG_PROC_FS)
@@ -1948,29 +1938,20 @@ static int bcm_release(struct socket *sock)
bcm_remove_op(op);
}
- list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
- /*
- * Don't care if we're bound or not (due to netdev problems)
- * can_rx_unregister() is always a save thing to do here.
- */
+ list_for_each_entry(op, &bo->rx_ops, list) {
if (op->ifindex) {
- /*
- * Only remove subscriptions that had not
- * been removed due to NETDEV_UNREGISTER
- * in bcm_notifier()
- *
- * op->rx_reg_dev is a tracked reference taken
- * when the subscription was registered, so it
- * stays valid here even if a concurrent
- * NETDEV_UNREGISTER already unlisted the device.
+ /* Only remove subscriptions that had not been removed
+ * in bcm_notify(). op->rx_reg_dev keeps the device's
+ * receive lists accessible.
*/
if (op->rx_reg_dev)
bcm_rx_unreg(op->rx_reg_dev, op);
- } else
+ } else {
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
-
+ }
+ cond_resched();
}
synchronize_rcu();
@@ -2024,7 +2005,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
ret = -ENODEV;
goto fail;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
dev_put(dev);
ret = -ENODEV;
goto fail;
@@ -2040,8 +2021,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
#if IS_ENABLED(CONFIG_PROC_FS)
if (net->can.bcmproc_dir) {
- /* unique socket address as filename */
- sprintf(bo->procname, "%llu", sock_i_ino(sk));
+ /* use unique socket inode number as filename */
+ snprintf(bo->procname, sizeof(bo->procname),
+ "%llu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
net->can.bcmproc_dir,
bcm_proc_show, sk);
diff --git a/net/can/gw.c b/net/can/gw.c
index 5793cb2420ed3..8a5327d8a00e8 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -52,6 +52,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/gw.h>
@@ -579,7 +580,7 @@ static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
{
return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
- gwj, "gw", NULL);
+ gwj, 0, NULL);
}
static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
@@ -612,7 +613,7 @@ static int cgw_notifier(struct notifier_block *nb,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg == NETDEV_UNREGISTER) {
@@ -1163,7 +1164,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!gwj->src.dev)
goto out;
- if (gwj->src.dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(gwj->src.dev))
goto out;
gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
@@ -1171,7 +1172,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!gwj->dst.dev)
goto out;
- if (gwj->dst.dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(gwj->dst.dev))
goto out;
/* is sending the skb back to the incoming interface intended? */
diff --git a/net/can/isotp.c b/net/can/isotp.c
index f5dc9d04bd686..9386389cd702e 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -65,6 +65,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/isotp.h>
@@ -755,6 +756,14 @@ 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 */
@@ -1488,11 +1497,11 @@ static int isotp_release(struct socket *sock)
list_del(&so->notifier);
spin_unlock(&isotp_notifier_lock);
- rtnl_lock();
lock_sock(sk);
/* remove current filters & unregister
- * tracked reference so->dev is taken at bind() time with rtnl_lock
+ * the tracked reference so->dev keeps the device's receive lists
+ * accessible, even after a namespace change
*/
if (so->bound && so->dev) {
if (isotp_register_rxid(so))
@@ -1510,8 +1519,6 @@ static int isotp_release(struct socket *sock)
so->bound = 0;
so->dev = NULL;
- rtnl_unlock();
-
/* Always wait for a grace period before touching the timers below.
* A concurrent NETDEV_UNREGISTER may have already unregistered our
* filters and cleared so->bound in isotp_notify() without waiting
@@ -1579,7 +1586,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
if (!addr->can_ifindex)
return -ENODEV;
- rtnl_lock();
+ /* lock_sock() serializes the binding with isotp_notify() */
lock_sock(sk);
if (so->bound) {
@@ -1605,34 +1612,44 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
goto out;
}
- dev = dev_get_by_index(net, addr->can_ifindex);
- if (!dev) {
- err = -ENODEV;
- goto out;
- }
- if (dev->type != ARPHRD_CAN) {
+ /* Publish the device inside the RCU section of the lookup: When it
+ * is unlisted concurrently, either isotp_notify() sees so->dev after
+ * synchronize_net() or the lookup fails. Check the device before, so
+ * that bind() can not fail after so->dev has been published.
+ */
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(net, addr->can_ifindex);
+ if (!dev || !can_get_ml_priv(dev))
err = -ENODEV;
- goto out_put_dev;
- }
- if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+ else if (READ_ONCE(dev->mtu) < so->ll.mtu)
err = -EINVAL;
- goto out_put_dev;
+ if (!err) {
+ dev_hold(dev);
+ WRITE_ONCE(so->dev, dev);
}
- if (!(dev->flags & IFF_UP))
+ rcu_read_unlock();
+ if (err)
+ goto out;
+
+ /* Either isotp_notify() sees so->dev on NETDEV_DOWN or we see the
+ * cleared IFF_UP, pairs with isotp_notify()
+ */
+ smp_mb();
+ if (!(READ_ONCE(dev->flags) & IFF_UP))
notify_enetdown = 1;
ifindex = dev->ifindex;
if (isotp_register_rxid(so))
can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
- isotp_rcv, sk, "isotp", sk);
+ isotp_rcv, sk, sock_i_ino(sk), sk);
/* no consecutive frame echo skb in flight */
WRITE_ONCE(so->cfecho, 0);
/* register for echo skb's */
can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
- isotp_rcv_echo, sk, "isotpe", sk);
+ isotp_rcv_echo, sk, sock_i_ino(sk), sk);
/* switch to new settings */
so->ifindex = ifindex;
@@ -1644,15 +1661,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
* can safely reach the device later, even if a concurrent
* NETDEV_UNREGISTER has already unlisted it by ifindex.
*/
- so->dev = dev;
netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL);
-out_put_dev:
- /* remove potential reference from dev_get_by_index() */
+ /* remove reference from the device lookup */
dev_put(dev);
out:
release_sock(sk);
- rtnl_unlock();
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
@@ -1855,12 +1869,21 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
- if (so->dev != dev)
+ if (msg == NETDEV_DOWN)
+ smp_mb(); /* pairs with isotp_bind(), see raw_notify() */
+
+ /* lockless fast path, see raw_notify() */
+ if (READ_ONCE(so->dev) != dev)
return;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
+ if (so->dev != dev) {
+ release_sock(sk);
+ break;
+ }
+
/* remove current filters & unregister */
if (so->bound) {
if (isotp_register_rxid(so))
@@ -1876,7 +1899,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
so->ifindex = 0;
so->bound = 0;
- so->dev = NULL;
+ WRITE_ONCE(so->dev, NULL);
release_sock(sk);
sk->sk_err = ENODEV;
@@ -1897,7 +1920,7 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
index 5e5e6c228f225..d9384cf0e356f 100644
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -184,7 +184,7 @@ static int j1939_can_rx_register(struct j1939_priv *priv)
j1939_priv_get(priv);
ret = can_rx_register(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,
- j1939_can_recv, priv, "j1939", NULL);
+ j1939_can_recv, priv, 0, NULL);
if (ret < 0) {
j1939_priv_put(priv);
return ret;
diff --git a/net/can/proc.c b/net/can/proc.c
index de4d05ae34597..b7496d2a37cd1 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -76,16 +76,39 @@ static const char rx_list_name[][8] = {
* af_can statistics stuff
*/
+static void can_reset_pkg_stats(struct can_pkg_stats *pkg_stats)
+{
+ atomic_long_set(&pkg_stats->rx_frames, 0);
+ atomic_long_set(&pkg_stats->tx_frames, 0);
+ atomic_long_set(&pkg_stats->matches, 0);
+
+ pkg_stats->total_rx_rate = 0;
+ pkg_stats->total_tx_rate = 0;
+ pkg_stats->total_rx_match_ratio = 0;
+
+ pkg_stats->current_rx_rate = 0;
+ pkg_stats->current_tx_rate = 0;
+ pkg_stats->current_rx_match_ratio = 0;
+
+ pkg_stats->max_rx_rate = 0;
+ pkg_stats->max_tx_rate = 0;
+ pkg_stats->max_rx_match_ratio = 0;
+
+ atomic_long_set(&pkg_stats->rx_frames_delta, 0);
+ atomic_long_set(&pkg_stats->tx_frames_delta, 0);
+ atomic_long_set(&pkg_stats->matches_delta, 0);
+}
+
static void can_init_stats(struct net *net)
{
struct can_pkg_stats *pkg_stats = net->can.pkg_stats;
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
/*
- * This memset function is called from a timer context (when
+ * This stats reset is called from a timer context (when
* can_stattimer is active which is the default) OR in a process
* context (reading the proc_fs when can_stattimer is disabled).
*/
- memset(pkg_stats, 0, sizeof(struct can_pkg_stats));
+ can_reset_pkg_stats(pkg_stats);
pkg_stats->jiffies_init = jiffies;
rcv_lists_stats->stats_reset++;
@@ -121,29 +144,23 @@ void can_stat_update(struct timer_list *t)
long rx_frames = atomic_long_read(&pkg_stats->rx_frames);
long tx_frames = atomic_long_read(&pkg_stats->tx_frames);
long matches = atomic_long_read(&pkg_stats->matches);
- long rx_frames_delta = atomic_long_read(&pkg_stats->rx_frames_delta);
- long tx_frames_delta = atomic_long_read(&pkg_stats->tx_frames_delta);
- long matches_delta = atomic_long_read(&pkg_stats->matches_delta);
+ /* fetch and clear values for 'current rate' calculation */
+ long rx_frames_delta = atomic_long_xchg(&pkg_stats->rx_frames_delta, 0);
+ long tx_frames_delta = atomic_long_xchg(&pkg_stats->tx_frames_delta, 0);
+ long matches_delta = atomic_long_xchg(&pkg_stats->matches_delta, 0);
- /* restart counting in timer context on user request */
- if (user_reset)
- can_init_stats(net);
-
- /* restart counting on jiffies overflow */
- if (j < pkg_stats->jiffies_init)
- can_init_stats(net);
-
- /* prevent overflow in calc_rate() */
- if (rx_frames > (LONG_MAX / HZ))
- can_init_stats(net);
-
- /* prevent overflow in calc_rate() */
- if (tx_frames > (LONG_MAX / HZ))
- can_init_stats(net);
-
- /* matches overflow - very improbable */
- if (matches > (LONG_MAX / 100))
+ /*
+ * Restart counting in timer context on user request, on jiffies
+ * overflow or to prevent overflows in calc_rate() and in the
+ * match ratio calculation. The local snapshots are stale after
+ * the reset, so skip the calculation for this interval.
+ */
+ if (user_reset || j < pkg_stats->jiffies_init ||
+ rx_frames > (LONG_MAX / HZ) || tx_frames > (LONG_MAX / HZ) ||
+ matches > (LONG_MAX / 100)) {
can_init_stats(net);
+ goto out;
+ }
/* calc total values */
if (rx_frames)
@@ -172,11 +189,7 @@ void can_stat_update(struct timer_list *t)
if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio)
pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio;
- /* clear values for 'current rate' calculation */
- atomic_long_set(&pkg_stats->tx_frames_delta, 0);
- atomic_long_set(&pkg_stats->rx_frames_delta, 0);
- atomic_long_set(&pkg_stats->matches_delta, 0);
-
+out:
/* restart timer (one second) */
mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ));
}
@@ -191,26 +204,32 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
struct receiver *r;
hlist_for_each_entry_rcu(r, rx_list, list) {
+ char symcheck[4];
char *fmt = (r->can_id & CAN_EFF_FLAG)?
- " %-5s %08x %08x %pK %pK %8ld %s\n" :
- " %-5s %03x %08x %pK %pK %8ld %s\n";
+ " %6s %08x %08x %8ld %10llu" :
+ " %6s %03x %08x %8ld %10llu";
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
- r->func, r->data, atomic_long_read(&r->matches),
- r->ident);
+ atomic_long_read(&r->matches), r->ino);
+
+ /* %ps falls back to the raw text address when the symbol
+ * can not be resolved (KALLSYMS=n, stripped module symbols)
+ */
+ snprintf(symcheck, sizeof(symcheck), "%ps", r->func);
+ if (strncmp(symcheck, "0x", 2))
+ seq_printf(m, " %ps\n", r->func);
+ else
+ seq_puts(m, " (unknown)\n");
}
}
static void can_print_recv_banner(struct seq_file *m)
{
/*
- * can1. 00000000 00000000 00000000
- * ....... 0 tp20
+ * device can_id can_mask matches sock_inode function
+ * vcan0 000 00000000 0 43798 raw_rcv [can_raw]
*/
- if (IS_ENABLED(CONFIG_64BIT))
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
- else
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
+ seq_puts(m, " device can_id can_mask matches sock_inode function\n");
}
static int can_stats_proc_show(struct seq_file *m, void *v)
diff --git a/net/can/raw.c b/net/can/raw.c
index 0acd4f6c6dd67..d07f3bb68cd5c 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -85,6 +85,7 @@ struct raw_sock {
struct sock sk;
struct net_device *dev;
netdevice_tracker dev_tracker;
+ struct net_device *bind_dev; /* hint for raw_notify(), no reference */
struct list_head notifier;
int ifindex;
unsigned int bound:1;
@@ -224,9 +225,10 @@ static int raw_enable_filters(struct net *net, struct net_device *dev,
int i;
for (i = 0; i < count; i++) {
+ cond_resched();
err = can_rx_register(net, dev, filter[i].can_id,
filter[i].can_mask,
- raw_rcv, sk, "raw", sk);
+ raw_rcv, sk, sock_i_ino(sk), sk);
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
@@ -247,7 +249,7 @@ static int raw_enable_errfilter(struct net *net, struct net_device *dev,
if (err_mask)
err = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,
- raw_rcv, sk, "raw", sk);
+ raw_rcv, sk, sock_i_ino(sk), sk);
return err;
}
@@ -258,9 +260,11 @@ static void raw_disable_filters(struct net *net, struct net_device *dev,
{
int i;
- for (i = 0; i < count; i++)
+ for (i = 0; i < count; i++) {
can_rx_unregister(net, dev, filter[i].can_id,
filter[i].can_mask, raw_rcv, sk);
+ cond_resched();
+ }
}
static inline void raw_disable_errfilter(struct net *net,
@@ -305,16 +309,31 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
struct net_device *dev)
{
struct sock *sk = &ro->sk;
+ struct net_device *bind_dev;
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
- if (ro->dev != dev)
+ if (msg == NETDEV_DOWN)
+ smp_mb(); /* IFF_UP is cleared before, pairs with raw_bind() */
+
+ /* Lockless fast path, ro->bind_dev covers a concurrent bind().
+ * Read it before ro->dev, pairs with smp_store_release() in
+ * raw_bind(). A stale ro->bind_dev only costs a lock_sock().
+ */
+ bind_dev = smp_load_acquire(&ro->bind_dev);
+ if (READ_ONCE(ro->dev) != dev &&
+ (msg != NETDEV_UNREGISTER || bind_dev != dev))
return;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
+ if (ro->dev != dev) {
+ release_sock(sk);
+ break;
+ }
+
/* remove current filters & unregister */
if (ro->bound) {
raw_disable_allfilters(dev_net(dev), dev, sk);
@@ -326,7 +345,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
ro->ifindex = 0;
ro->bound = 0;
- ro->dev = NULL;
+ WRITE_ONCE(ro->dev, NULL);
ro->count = 0;
release_sock(sk);
@@ -348,7 +367,7 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
@@ -381,6 +400,7 @@ static int raw_init(struct sock *sk)
ro->bound = 0;
ro->ifindex = 0;
ro->dev = NULL;
+ ro->bind_dev = NULL;
/* set default filter to single entry dfilter */
ro->dfilter.can_id = 0;
@@ -431,13 +451,15 @@ static int raw_release(struct socket *sock)
list_del(&ro->notifier);
spin_unlock(&raw_notifier_lock);
- rtnl_lock();
lock_sock(sk);
- /* remove current filters & unregister */
+ /* remove current filters & unregister
+ * the tracked reference ro->dev keeps the device's receive lists
+ * accessible, even after a namespace change
+ */
if (ro->bound) {
if (ro->dev) {
- raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
+ raw_disable_allfilters(net, ro->dev, sk);
netdev_put(ro->dev, &ro->dev_tracker);
} else {
raw_disable_allfilters(net, NULL, sk);
@@ -456,7 +478,6 @@ static int raw_release(struct socket *sock)
sock->sk = NULL;
release_sock(sk);
- rtnl_unlock();
sock_prot_inuse_add(net, sk->sk_prot, -1);
sock_put(sk);
@@ -479,26 +500,34 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
if (addr->can_family != AF_CAN)
return -EINVAL;
- rtnl_lock();
+ /* lock_sock() serializes the binding with raw_notify() */
lock_sock(sk);
if (ro->bound && addr->can_ifindex == ro->ifindex)
goto out;
if (addr->can_ifindex) {
- dev = dev_get_by_index(sock_net(sk), addr->can_ifindex);
+ /* Publish the device inside the RCU section of the lookup:
+ * When it is unlisted concurrently, either raw_notify() sees
+ * ro->bind_dev after synchronize_net() or the lookup fails.
+ */
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(sock_net(sk), addr->can_ifindex);
+ if (dev) {
+ dev_hold(dev);
+ /* pairs with smp_load_acquire() in raw_notify() */
+ smp_store_release(&ro->bind_dev, dev);
+ }
+ rcu_read_unlock();
if (!dev) {
err = -ENODEV;
goto out;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
err = -ENODEV;
goto out_put_dev;
}
- if (!(dev->flags & IFF_UP))
- notify_enetdown = 1;
-
ifindex = dev->ifindex;
/* filters set by default/setsockopt */
@@ -517,7 +546,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
if (ro->bound) {
/* unregister old filters */
if (ro->dev) {
- raw_disable_allfilters(dev_net(ro->dev),
+ raw_disable_allfilters(sock_net(sk),
ro->dev, sk);
/* drop reference to old ro->dev */
netdev_put(ro->dev, &ro->dev_tracker);
@@ -528,17 +557,24 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
ro->ifindex = ifindex;
ro->bound = 1;
/* bind() ok -> hold a reference for new ro->dev */
- ro->dev = dev;
- if (ro->dev)
- netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
+ WRITE_ONCE(ro->dev, dev);
+ netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
+
+ if (dev) {
+ /* Either raw_notify() sees ro->dev on NETDEV_DOWN or
+ * we see the cleared IFF_UP, pairs with raw_notify()
+ */
+ smp_mb();
+ if (!(READ_ONCE(dev->flags) & IFF_UP))
+ notify_enetdown = 1;
+ }
}
out_put_dev:
- /* remove potential reference from dev_get_by_index() */
+ /* remove reference from the device lookup */
dev_put(dev);
out:
release_sock(sk);
- rtnl_unlock();
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
@@ -567,58 +603,41 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
}
static int raw_setsockopt_locked(struct socket *sock, int optname,
- sockptr_t optval, unsigned int optlen)
+ sockptr_t optval, unsigned int optlen,
+ struct can_filter *filter,
+ struct can_filter *sfilter, int count)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
- struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
- struct can_filter sfilter; /* single filter */
struct net_device *dev = NULL;
can_err_mask_t err_mask = 0;
- int count = 0;
int flag;
int err = 0;
switch (optname) {
case CAN_RAW_FILTER:
- if (optlen % sizeof(struct can_filter) != 0)
- return -EINVAL;
-
- if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
- return -EINVAL;
-
- count = optlen / sizeof(struct can_filter);
-
- if (count > 1) {
- /* filter does not fit into dfilter => alloc space */
- filter = memdup_sockptr(optval, optlen);
- if (IS_ERR(filter))
- return PTR_ERR(filter);
- } else if (count == 1) {
- if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
- return -EFAULT;
- }
-
+ /* @filter has been copied by raw_setsockopt(). It is linked
+ * to the socket on success and freed by the caller on error.
+ *
+ * The reg_state check is only a shortcut: On a concurrent
+ * unregistration or namespace change raw_notify() waits for
+ * lock_sock() and removes the filters set here.
+ */
dev = ro->dev;
- if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
- if (count > 1)
- kfree(filter);
+ if (ro->bound && dev &&
+ READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
return -ENODEV;
- }
if (ro->bound) {
/* (try to) register the new filters */
if (count == 1)
err = raw_enable_filters(sock_net(sk), dev, sk,
- &sfilter, 1);
+ sfilter, 1);
else
err = raw_enable_filters(sock_net(sk), dev, sk,
filter, count);
- if (err) {
- if (count > 1)
- kfree(filter);
+ if (err)
return err;
- }
/* remove old filter registrations */
raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
@@ -632,7 +651,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
/* link new filters to the socket */
if (count == 1) {
/* copy filter data for single filter */
- ro->dfilter = sfilter;
+ ro->dfilter = *sfilter;
filter = &ro->dfilter;
}
ro->filter = filter;
@@ -648,8 +667,10 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
err_mask &= CAN_ERR_MASK;
+ /* lockless shortcut, see CAN_RAW_FILTER */
dev = ro->dev;
- if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
+ if (ro->bound && dev &&
+ READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
return -ENODEV;
/* remove current error mask */
@@ -751,18 +772,47 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
+ struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
+ struct can_filter sfilter; /* single filter */
+ int count = 0;
int err;
if (level != SOL_CAN_RAW)
return -EINVAL;
- rtnl_lock();
+ if (optname == CAN_RAW_FILTER) {
+ /* Copy the filters before lock_sock(), so a stalled copy can
+ * not block raw_notify() waiting for lock_sock() under RTNL.
+ */
+ if (optlen % sizeof(struct can_filter) != 0)
+ return -EINVAL;
+
+ if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
+ return -EINVAL;
+
+ count = optlen / sizeof(struct can_filter);
+
+ if (count > 1) {
+ /* filter does not fit into dfilter => alloc space */
+ filter = memdup_sockptr(optval, optlen);
+ if (IS_ERR(filter))
+ return PTR_ERR(filter);
+ } else if (count == 1) {
+ if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
+ return -EFAULT;
+ }
+ }
+
lock_sock(sk);
- err = raw_setsockopt_locked(sock, optname, optval, optlen);
+ err = raw_setsockopt_locked(sock, optname, optval, optlen,
+ filter, &sfilter, count);
release_sock(sk);
- rtnl_unlock();
+
+ /* on error free the filters allocated for CAN_RAW_FILTER above */
+ if (err && count > 1)
+ kfree(filter);
return err;
}
|
| 3/2 | 2026/10/22 11:37 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20631 output: 29 thoughts: 1697 Thoughts: **Analysis of the CAN Networking Patch for Syzkaller Fuzzing** Okay, let's dive into this patch series. My initial thought is to figure out if this warrants a syzkaller fuzzing campaign. The prompt is clear, so let's start by looking at what the patch touches. It appears to be deep within the SocketCAN realm, including core structures, drivers, and various socket types (raw, BCM, ISO-TP). Good, it touches a broad range of areas. Specifically, the affected files cover `Documentation/networking/can.rst`, `drivers/net/can/dev/skb.c`, `include/linux/can/can-ml.h`, `include/linux/can/core.h`, `include/linux/can/dev.h`, `include/linux/can/skb.h`, `include/net/netns/can.h`, `net/can/af_can.c`, `net/can/af_can.h`, `net/can/bcm.c`, `net/can/gw.c`, `net/can/isotp.c`, `net/can/proc.c`, and `net/can/raw.c`. This is all networking code dealing with SocketCAN and its various CAN drivers. Excellent, it appears this touches core networking. I'm thinking, "Can syzkaller handle this?". Yes, syzkaller is already fuzzing CAN sockets, virtual CAN devices like vcan/vxcan, and it even has definitions in syzlang. This is a very good sign. Alright, let's examine the changes more closely. I see modifications related to: 1. **SKB Allocation/Freeing:** Changes from `kfree_skb` to `dev_kfree_skb_any` or `dev_consume_skb_any`. Also, a check of `!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb)`. Good. This could be interesting. 2. **Multicast Listeners:** Adding `spinlock_t lock;` to `struct can_dev_rcv_lists` and initializing it. This points to a change in locking around receive lists. 3. **Synchronization and Locking:** The big one. Changing `can_rx_register` and `can_rx_unregister` to use per-device locks (`dev_rcv_lists->lock`) instead of a global lock. Also, removing `if (dev && !net_eq(net, dev_net(dev))) return;` is a change. This is a huge point. Now locking is being handled per-device rather than globally. 4. **BCM Changes:** Updates to proc output and replacing `list_for_each_entry_safe` with `list_for_each_entry` and adding `cond_resched()`. Also using `sock_i_ino(sk)`. 5. **ISOTP Tweaks:** Adding a CAN MTU check, removing `rtnl_lock()` from `isotp_release`, and `isotp_bind`, and introducing `dev_get_by_index_rcu`, memory barriers, and a lockless fast path. More synchronization changes. 6. **Proc Statistics:** Changes to atomic operations and symbol printing. 7. **Raw Socket Updates:** Removing `rtnl_lock()` from `raw_release`, `raw_bind`, `raw_setsockopt`, and adding lockless fast paths, memory barriers, allocation of memory before locking, and `cond_resched()`. Wow! This patch is a major refactoring of concurrency and synchronization within the CAN stack. It's removing `rtnl_lock`, replacing it with fine-grained locking, introducing lockless RCU/barriers, handling netns, and revising notifier handling. This is a prime target for syzkaller. This is exactly the type of code that syzkaller excels at testing: concurrency refactorings, lock removal (replacing with fine-grained locking and lockless RCU/barriers), netns handling, and notifier handling. This is very good. Race conditions, UAFs, memory ordering bugs, deadlocks, and lockdep warnings are precisely the types of issues that surface in such patches. Therefore, WorthFuzzing is a definite **true**. We need to look at specific kernel configs and the key functions to focus on with this. |
| 4/2 | 2026/10/22 11:37 | tool | grepper |
0mArgs:{
"Expression": "config CAN",
"PathPrefixes": [
"net/can/Kconfig"
]
}Results: {
"Output": "net/can/Kconfig-5-\nnet/can/Kconfig:6:menuconfig CAN\nnet/can/Kconfig-7-\ttristate \"CAN bus subsystem support\"\n--\nnet/can/Kconfig=22=if CAN\nnet/can/Kconfig-23-\nnet/can/Kconfig:24:config CAN_RAW\nnet/can/Kconfig-25-\ttristate \"Raw CAN Protocol (raw access with CAN-ID filtering)\"\n--\nnet/can/Kconfig-33-\nnet/can/Kconfig:34:config CAN_BCM\nnet/can/Kconfig-35-\ttristate \"Broadcast Manager CAN Protocol (with content filtering)\"\n--\nnet/can/Kconfig-45-\nnet/can/Kconfig:46:config CAN_GW\nnet/can/Kconfig-47-\ttristate \"CAN Gateway/Router (with netlink configuration)\"\n--\nnet/can/Kconfig=57=source \"net/can/j1939/Kconfig\"\nnet/can/Kconfig-58-\nnet/can/Kconfig:59:config CAN_ISOTP\nnet/can/Kconfig-60-\ttristate \"ISO 15765-2 CAN transport protocol\"\n"
} |
| 5/2 | 2026/10/22 11:37 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23638 output: 30 thoughts: 26 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating CAN Driver Configuration** Right, let's dive in. My immediate thought is to zero in on the CAN driver configuration, which means I need to start with the obvious places: `drivers/net/can/Kconfig` and `drivers/net/can/dev/Makefile` or perhaps a related `Kconfig` file in the `dev` directory. I'm assuming there's been a reported issue or a new feature request and those files will tell the story. I'll need to check the Kconfig files for configuration options and dependencies. The Makefile (or similar) will tell me the build process and what modules are included, allowing me to understand which CAN devices are supported and how they are handled at a source level, and possibly where problems could originate. I'm already anticipating the typical setup and dependencies so that the issue may quickly be resolved. Let's see what we're looking at. |
| 6/2 | 2026/10/22 11:37 | tool | grepper |
0mArgs:{
"Expression": "CAN_DEV",
"PathPrefixes": [
"drivers/net/can/"
]
}Results: {
"Output": "drivers/net/can/Kconfig-2-\ndrivers/net/can/Kconfig:3:menuconfig CAN_DEV\ndrivers/net/can/Kconfig-4-\ttristate \"CAN Device Drivers\"\n--\ndrivers/net/can/Kconfig-22-\ndrivers/net/can/Kconfig:23:if CAN_DEV\ndrivers/net/can/Kconfig-24-\n--\ndrivers/net/can/Kconfig=263=config CAN_DEBUG_DEVICES\n--\ndrivers/net/can/Kconfig-270-\ndrivers/net/can/Kconfig:271:endif #CAN_DEV\n--\ndrivers/net/can/dev/Makefile-2-\ndrivers/net/can/dev/Makefile:3:obj-$(CONFIG_CAN_DEV) += can-dev.o\ndrivers/net/can/dev/Makefile-4-\n--\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c=25=MODULE_DESCRIPTION(\"CAN driver for Kvaser CAN/PCIe devices\");\n--\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-49-/* Xilinx based devices */\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c:50:#define KVASER_PCIEFD_M2_4CAN_DEVICE_ID 0x0017\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c:51:#define KVASER_PCIEFD_8CAN_DEVICE_ID 0x0019\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-52-\n--\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c=403=static struct pci_device_id kvaser_pciefd_id_table[] = {\n--\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-444-\t{\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c:445:\t\tPCI_DEVICE(KVASER_PCIEFD_VENDOR, KVASER_PCIEFD_M2_4CAN_DEVICE_ID),\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-446-\t\t.driver_data = (kernel_ulong_t)\u0026kvaser_pciefd_xilinx_driver_data,\n--\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-448-\t{\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c:449:\t\tPCI_DEVICE(KVASER_PCIEFD_VENDOR, KVASER_PCIEFD_8CAN_DEVICE_ID),\ndrivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c-450-\t\t.driver_data = (kernel_ulong_t)\u0026kvaser_pciefd_xilinx_driver_data,\n--\ndrivers/net/can/sja1000/plx_pci.c=32=struct plx_pci_card {\n--\ndrivers/net/can/sja1000/plx_pci.c-130-#define ASEM_RAW_CAN_VENDOR_ID\t\t0x10b5\ndrivers/net/can/sja1000/plx_pci.c:131:#define ASEM_RAW_CAN_DEVICE_ID\t\t0x9030\ndrivers/net/can/sja1000/plx_pci.c-132-#define ASEM_RAW_CAN_SUB_VENDOR_ID\t0x3000\n--\ndrivers/net/can/sja1000/plx_pci.c=272=static const struct pci_device_id plx_pci_tbl[] = {\n--\ndrivers/net/can/sja1000/plx_pci.c-347-\t\t/* ASEM Dual CAN raw */\ndrivers/net/can/sja1000/plx_pci.c:348:\t\tPCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,\ndrivers/net/can/sja1000/plx_pci.c-349-\t\t\t ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID),\n--\ndrivers/net/can/sja1000/plx_pci.c-352-\t\t/* ASEM Dual CAN raw -new model */\ndrivers/net/can/sja1000/plx_pci.c:353:\t\tPCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,\ndrivers/net/can/sja1000/plx_pci.c-354-\t\t\t ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS),\n--\ndrivers/net/can/usb/ucan.c=73=enum {\ndrivers/net/can/usb/ucan.c:74:\tUCAN_DEVICE_GET_FW_STRING = 0,\ndrivers/net/can/usb/ucan.c-75-};\n--\ndrivers/net/can/usb/ucan.c=426=static void ucan_get_fw_str(struct ucan_priv *up, char *fw_str, size_t size)\n--\ndrivers/net/can/usb/ucan.c-430-\tret = usb_control_msg(up-\u003eudev, usb_rcvctrlpipe(up-\u003eudev, 0),\ndrivers/net/can/usb/ucan.c:431:\t\t\t UCAN_DEVICE_GET_FW_STRING,\ndrivers/net/can/usb/ucan.c-432-\t\t\t USB_DIR_IN | USB_TYPE_VENDOR |\n"
} |
| 7/2 | 2026/10/22 11:37 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25463 output: 38 thoughts: 14 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Virtual CAN Driver Exploration** Okay, so I need to get my hands dirty with some virtual CAN setups. I'm thinking I should start by taking a close look at the established options, primarily `vcan` and `vxcan`. My immediate focus is going to be on their core implementation differences, specifically their handling of socketCAN's underlying mechanics. I'm interested in how they emulate the CAN bus behavior, how they handle frame transmission and reception, and how well they integrate with common CAN utilities and protocol analysis tools. Specifically, I want to see if the recent updates to the kernel had any impacts on their operation, looking for potential performance bottlenecks or quirks introduced. I'll need to benchmark them under different simulated loads and traffic patterns to get a feel for their throughput and latency characteristics in both a single-node setup, and a multi-node simulation. I'll also check their support for extended CAN frames and error handling capabilities. Ultimately, I need to determine which virtual driver is the better fit for my specific requirements. |
| 8/2 | 2026/10/22 11:38 | tool | grepper |
0mArgs:{
"Expression": "config CAN_VCAN|config CAN_VXCAN",
"PathPrefixes": [
"drivers/net/can/"
]
}Results: {
"Output": "drivers/net/can/Kconfig=23=if CAN_DEV\ndrivers/net/can/Kconfig-24-\ndrivers/net/can/Kconfig:25:config CAN_VCAN\ndrivers/net/can/Kconfig-26-\ttristate \"Virtual Local CAN Interface (vcan)\"\n--\ndrivers/net/can/Kconfig-33-\ndrivers/net/can/Kconfig:34:config CAN_VXCAN\ndrivers/net/can/Kconfig-35-\ttristate \"Virtual CAN Tunnel (vxcan)\"\n"
} |
| 9/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25888 output: 31 thoughts: 18 Thoughts: Okay, let's dive into this. Here's how I'd approach that particular line of inquiry, thinking like a seasoned embedded systems developer: **Investigating the CAN Compilation Process** Alright, the objective is clear: I need to understand what's being compiled for the CAN interface. My initial instinct is to go straight to the source, the build system's configuration. In this case, `net/can/Makefile` is the place to start. That Makefile is my map for understanding the compilation process for CAN-related components. It's where the rubber meets the road, so to speak. By examining this file, I expect to see a list of object files (.o files) that are being built. These objects represent the compiled source code modules for CAN functionality. This could be drivers, protocol implementations, or anything related to the network interface card. From there, I can understand the bigger picture of what pieces make up the whole, and how they relate. This is a critical first step. |
| 10/2 | 2026/10/22 11:38 | tool | read-file |
0mArgs:{
"File": "net/can/Makefile",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t#\n 3:\t# Makefile for the Linux Controller Area Network core.\n 4:\t#\n 5:\t\n 6:\tobj-$(CONFIG_CAN)\t+= can.o\n 7:\tcan-y\t\t\t:= af_can.o\n 8:\tcan-$(CONFIG_PROC_FS)\t+= proc.o\n 9:\t\n 10:\tobj-$(CONFIG_CAN_RAW)\t+= can-raw.o\n 11:\tcan-raw-y\t\t:= raw.o\n 12:\t\n 13:\tobj-$(CONFIG_CAN_BCM)\t+= can-bcm.o\n 14:\tcan-bcm-y\t\t:= bcm.o\n 15:\t\n 16:\tobj-$(CONFIG_CAN_GW)\t+= can-gw.o\n 17:\tcan-gw-y\t\t:= gw.o\n 18:\t\n 19:\tobj-$(CONFIG_CAN_J1939)\t+= j1939/\n 20:\t\n 21:\tobj-$(CONFIG_CAN_ISOTP)\t+= can-isotp.o\n 22:\tcan-isotp-y\t\t:= isotp.o\n"
} |
| 11/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26476 output: 35 thoughts: 209 Thoughts: **Analyzing CAN Bus Module Configuration** Okay, here's what I'm seeing: there's a clear breakdown of how the CAN bus modules are built based on the kernel configuration. The `obj-$(CONFIG_CAN) += can.o` line, followed by related objects (af_can.o, proc.o), tells me that enabling the base `CONFIG_CAN` option brings in the core CAN functionality. Similarly, `CONFIG_CAN_RAW`, `CONFIG_CAN_BCM`, `CONFIG_CAN_GW`, and `CONFIG_CAN_J1939` each correspond to their respective modules (`can-raw.o`, `can-bcm.o`, `can-gw.o`, and `j1939/`), implying that these can be compiled into the kernel independently via the corresponding configuration options. Finally `CONFIG_CAN_ISOTP` introduces `can-isotp.o` and related dependency isotp.o. It's clean and organized, leveraging the conditional object assignment approach that is typical of Makefiles within the Linux kernel. I can see the modular design, allowing for a highly configurable CAN bus implementation. And in addition, based on the reference to `drivers/net/can/dev/Makefile`, I will need to verify this path and see what's the dependency here. This is important to understand where the base dependencies lie. The reference `obj-$(CONFIG_CAN_DEV) += can-dev.o` shows the module `can-dev.o` (which includes `skb.o`) is part of the `can-dev` driver. I need to make sure I understand the `can-dev` driver functionality as a component in a CAN system. So, I need to check `drivers/net/can/dev/Makefile` to understand the dependencies of `can-dev.o`. |
| 12/2 | 2026/10/22 11:38 | tool | read-file |
0mArgs:{
"File": "drivers/net/can/dev/Makefile",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t\n 3:\tobj-$(CONFIG_CAN_DEV) += can-dev.o\n 4:\t\n 5:\tcan-dev-y += skb.o\n 6:\t\n 7:\tcan-dev-$(CONFIG_CAN_CALC_BITTIMING) += calc_bittiming.o\n 8:\tcan-dev-$(CONFIG_CAN_NETLINK) += bittiming.o\n 9:\tcan-dev-$(CONFIG_CAN_NETLINK) += dev.o\n 10:\tcan-dev-$(CONFIG_CAN_NETLINK) += length.o\n 11:\tcan-dev-$(CONFIG_CAN_NETLINK) += netlink.o\n 12:\tcan-dev-$(CONFIG_CAN_RX_OFFLOAD) += rx-offload.o\n"
} |
| 13/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27337 output: 34 thoughts: 85 Thoughts: **Investigating the CAN Device Configuration Dependencies** Okay, so I'm looking at this build system and I see `can-dev-y += skb.o`. That means `skb.o` is going to be included in the `can-dev` build if the `CONFIG_CAN_DEV` option is set. Makes sense so far. Then I'm tracing back those dependencies, which will eventually be needed for the actual build process. Diving into the `Kconfig` files, I find that `CAN_DEV`, which our module depends on, itself depends on `CAN`. Further, `CAN_VCAN` and `CAN_VXCAN` also depend on `CAN_DEV`. So, the structure is clearly defined. But, to be sure, I should check whether `CAN_DEV` depends on `CAN`. It's a common dependency pattern, but I don't want to make assumptions. I need to confirm the relationship to ensure the correct modules get built and linked. I have to make sure that the configuration variables are set properly to include those modules. I will look for other potentially related dependencies while I am at it to avoid a compile error. |
| 14/2 | 2026/10/22 11:38 | tool | read-file |
0mArgs:{
"File": "drivers/net/can/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t\n 3:\tmenuconfig CAN_DEV\n 4:\t\ttristate \"CAN Device Drivers\"\n 5:\t\tdefault y\n 6:\t\tdepends on CAN\n 7:\t\thelp\n 8:\t\t Controller Area Network (CAN) is serial communications protocol up to\n 9:\t\t 1Mbit/s for its original release (now known as Classical CAN) and up\n 10:\t\t to 8Mbit/s for the more recent CAN with Flexible Data-Rate\n 11:\t\t (CAN-FD). The CAN bus was originally mainly for automotive, but is now\n 12:\t\t widely used in marine (NMEA2000), industrial, and medical\n 13:\t\t applications. More information on the CAN network protocol family\n 14:\t\t PF_CAN is contained in \u003cDocumentation/networking/can.rst\u003e.\n 15:\t\n 16:\t\t This section contains all the CAN(-FD) device drivers including the\n 17:\t\t virtual ones. If you own such devices or plan to use the virtual CAN\n 18:\t\t interfaces to develop applications, say Y here.\n 19:\t\n 20:\t\t To compile as a module, choose M here: the module will be called\n 21:\t\t can-dev.\n 22:\t\n 23:\tif CAN_DEV\n 24:\t\n 25:\tconfig CAN_VCAN\n 26:\t\ttristate \"Virtual Local CAN Interface (vcan)\"\n 27:\t\thelp\n 28:\t\t Similar to the network loopback devices, vcan offers a\n 29:\t\t virtual local CAN interface.\n 30:\t\n 31:\t\t This driver can also be built as a module. If so, the module\n 32:\t\t will be called vcan.\n 33:\t\n 34:\tconfig CAN_VXCAN\n 35:\t\ttristate \"Virtual CAN Tunnel (vxcan)\"\n 36:\t\thelp\n 37:\t\t Similar to the virtual ethernet driver veth, vxcan implements a\n 38:\t\t local CAN traffic tunnel between two virtual CAN network devices.\n 39:\t\t When creating a vxcan, two vxcan devices are created as pair.\n 40:\t\t When one end receives the packet it appears on its pair and vice\n"
} |
| 15/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28293 output: 33 thoughts: 3421 Thoughts: **Analysis of Synchronization and Locking Changes in the CAN Subsystem** Alright, let's break this down. My initial thought is to grab the commit message for more context, but even without that, the diff provides a pretty clear picture. We're looking at significant changes across several core CAN components. First, there are the usual memory management fixes, replacing `kfree_skb` with `dev_kfree_skb_any` in a few places to handle the potential for different DMA mappings and ensure proper skb deallocation. Nothing too exciting there, though worth noting. The real meat of this lies in the refactoring of the locking mechanisms within the CAN socket protocols, specifically `raw`, `isotp`, and `bcm`. It appears we are seeing a shift away from the global `net->can.rcvlists_lock` for receive list management towards per-device spinlocks (`dev_rcv_lists->lock`). This is a critical change. This per-device locking aims at improving concurrency, but it introduces a whole new class of race conditions, use-after-free conditions, deadlocks, and memory ordering issues if not handled carefully. I'm also seeing several critical removals of `rtnl_lock` and `rtnl_unlock` calls, particularly in `raw` and `isotp`. These are fundamental changes to how these protocols handle network configuration changes and socket operations. The introduction of lockless fast paths with memory barriers (`WRITE_ONCE`, `READ_ONCE`, `smp_store_release`, `smp_load_acquire`, `smp_mb`) is the key to performance, but these constructs are difficult to use and very prone to error. The fact that the CAN subsystem is exposed through PF_CAN (AF_CAN) sockets, including RAW, BCM, ISOTP, and GW, is key. And even more crucially, that virtual CAN devices (vcan, vxcan) are available to unprivileged users with user namespaces and even in a standard syzkaller setup, means these changes are *highly* exploitable. Syzkaller's standard templates should provide comprehensive coverage for CAN sockets. Moving devices between network namespaces with rtnetlink while sockets are open, plus interface up/down events, these are all standard syzkaller operations! Now, which functions are critical for fuzzing? Specifically, I'm focusing on the ones directly modified: `raw_bind`, `raw_release`, `raw_setsockopt`, `raw_setsockopt_locked`, `raw_notify`, `isotp_bind`, `isotp_release`, `isotp_notify`, `can_rx_register`, `can_rx_unregister`, `bcm_release`, `bcm_rx_unreg`. These are not ubiquitous functions, they're core to the subsystem, which makes them prime targets. They are also exported or have symbols in vmlinux or modules. The removal of `rtnl_lock` and the introduction of lockless operations means this code is practically begging to be raced. The fact that these functions are linked to specific config options such as `CAN`, `CAN_RAW`, `CAN_BCM`, `CAN_ISOTP`, etc., tells us how to set up the fuzzing environment. The patch, with all the synchronization changes, is a recipe for fuzzing success! |
| 16/2 | 2026/10/22 11:38 | tool | grepper |
0mArgs:{
"Expression": "config CAN_J1939",
"PathPrefixes": [
"net/can/"
]
}Results: {
"Output": "net/can/j1939/Kconfig-5-\nnet/can/j1939/Kconfig:6:config CAN_J1939\nnet/can/j1939/Kconfig-7-\ttristate \"SAE J1939\"\n"
} |
| 17/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32536 output: 32 thoughts: 63 Thoughts: Okay, here's my assessment of the situation: **Probing for CAN Subsystem Configuration Dependencies** Alright, before I dive too deep into this, I need to make sure I'm not missing any pieces of the puzzle. My first instinct is to perform a sanity check. Let me see... I need to verify that all the relevant CAN subsystem configurations are handled. That includes the usual suspects: `CAN`, `CAN_RAW`, `CAN_ISOTP`, `CAN_BCM`, `CAN_GW`, `CAN_DEV`, `CAN_VCAN` and whatever else might be lurking out there that's crucial to the system's proper operation. It's easy to overlook something so let's start there. Now I should look into `net/can/Kconfig`. |
| 18/2 | 2026/10/22 11:38 | tool | read-file |
0mArgs:{
"File": "net/can/Kconfig",
"FirstLine": 1,
"LineCount": 60
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# Controller Area Network (CAN) network layer core configuration\n 4:\t#\n 5:\t\n 6:\tmenuconfig CAN\n 7:\t\ttristate \"CAN bus subsystem support\"\n 8:\t\tselect SKB_EXTENSIONS\n 9:\t\thelp\n 10:\t\t Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial\n 11:\t\t communications protocol. Development of the CAN bus started in\n 12:\t\t 1983 at Robert Bosch GmbH, and the protocol was officially\n 13:\t\t released in 1986. The CAN bus was originally mainly for automotive,\n 14:\t\t but is now widely used in marine (NMEA2000), industrial, and medical\n 15:\t\t applications. More information on the CAN network protocol family\n 16:\t\t PF_CAN is contained in \u003cDocumentation/networking/can.rst\u003e.\n 17:\t\n 18:\t\t If you want CAN support you should say Y here and also to the\n 19:\t\t specific driver for your controller(s) under the Network device\n 20:\t\t support section.\n 21:\t\n 22:\tif CAN\n 23:\t\n 24:\tconfig CAN_RAW\n 25:\t\ttristate \"Raw CAN Protocol (raw access with CAN-ID filtering)\"\n 26:\t\tdefault y\n 27:\t\thelp\n 28:\t\t The raw CAN protocol option offers access to the CAN bus via\n 29:\t\t the BSD socket API. You probably want to use the raw socket in\n 30:\t\t most cases where no higher level protocol is being used. The raw\n 31:\t\t socket has several filter options e.g. ID masking / error frames.\n 32:\t\t To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.\n 33:\t\n 34:\tconfig CAN_BCM\n 35:\t\ttristate \"Broadcast Manager CAN Protocol (with content filtering)\"\n 36:\t\tdefault y\n 37:\t\thelp\n 38:\t\t The Broadcast Manager offers content filtering, timeout monitoring,\n 39:\t\t sending of RTR frames, and cyclic CAN messages without permanent user\n 40:\t\t interaction. The BCM can be 'programmed' via the BSD socket API and\n 41:\t\t informs you on demand e.g. only on content updates / timeouts.\n 42:\t\t You probably want to use the bcm socket in most cases where cyclic\n 43:\t\t CAN messages are used on the bus (e.g. in automotive environments).\n 44:\t\t To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.\n 45:\t\n 46:\tconfig CAN_GW\n 47:\t\ttristate \"CAN Gateway/Router (with netlink configuration)\"\n 48:\t\tdefault y\n 49:\t\thelp\n 50:\t\t The CAN Gateway/Router is used to route (and modify) CAN frames.\n 51:\t\t It is based on the PF_CAN core infrastructure for msg filtering and\n 52:\t\t msg sending and can optionally modify routed CAN frames on the fly.\n 53:\t\t CAN frames can be routed between CAN network interfaces (one hop).\n 54:\t\t They can be modified with AND/OR/XOR/SET operations as configured\n 55:\t\t by the netlink configuration interface known e.g. from iptables.\n 56:\t\n 57:\tsource \"net/can/j1939/Kconfig\"\n 58:\t\n 59:\tconfig CAN_ISOTP\n 60:\t\ttristate \"ISO 15765-2 CAN transport protocol\"\n"
} |
| 19/2 | 2026/10/22 11:38 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33734 output: 364 thoughts: 1009 Thoughts: **Analyzing the CAN Subsystem Modifications** Okay, so I'm diving into this CAN subsystem patch and immediately, it's clear that everything hinges on `CAN`. Specifically, I see that several configurations, `CAN_DEV`, `CAN_VCAN`, and `CAN_VXCAN`, are directly dependent on `CAN`. I also scanned the Kconfig to check for other CAN-related configs: `CONFIG_CAN`, `CONFIG_CAN_RAW`, `CONFIG_CAN_BCM`, `CONFIG_CAN_ISOTP`, `CONFIG_CAN_GW`, `CONFIG_CAN_J1939`, `CONFIG_CAN_DEV`, `CONFIG_CAN_VCAN`, and `CONFIG_CAN_VXCAN`. Now, let's identify the core logic that's been modified. I'm focusing on a relevant set of non-ubiquitous symbols. We are looking at: `can_rx_register`, `can_rx_unregister`, `raw_bind`, `raw_release`, `raw_setsockopt`, `raw_notify`, `isotp_bind`, `isotp_release`, `isotp_notify`, `isotp_rcv`, `bcm_release`, `bcm_rx_unreg`, `can_dropped_invalid_skb`, and `can_put_echo_skb`. Okay, double-checking where these are defined, I can see that `can_rx_register` and `can_rx_unregister` are in `net/can/af_can.c`, `raw_bind`, `raw_release`, `raw_setsockopt`, and `raw_notify` are in `net/can/raw.c`, `isotp_bind`, `isotp_release`, `isotp_notify`, and `isotp_rcv` are in `net/can/isotp.c`, `bcm_release` and `bcm_rx_unreg` are in `net/can/bcm.c`, and finally `can_dropped_invalid_skb` and `can_put_echo_skb` are in `drivers/net/can/dev/skb.c`. Great! None of these are ubiquitous. They are exclusively part of the CAN networking and device operations. Now, let's confirm the reasoning behind all this. This patch clearly touches the Linux CAN subsystem: specifically, `af_can`, `can-raw`, `can-isotp`, `can-bcm`, `can-gw`, `can-j1939`, and `can-dev`. And here's the kicker: this patch series is making significant locking and synchronization changes. It's replacing the global `rcvlists_lock` with per-device locks, removing `rtnl_lock` completely from some functions, introducing lockless RCU lookups and memory barriers, and moving memory allocation. Also, there are changes to `can_dropped_invalid_skb()` and skb cleanup paths. The key thing I'm seeing is that all this code is accessible from user space in virtualized environments via AF_CAN sockets (`CAN_RAW`, `CAN_BCM`, `CAN_ISOTP`) and virtual CAN network devices (`vcan`, `vxcan`). Given the complex lock removals, memory ordering changes, and concurrent netdev notifier handling, this has a high potential for race conditions, use-after-free, deadlocks, and protocol errors. This means it is highly valuable to fuzz with syzkaller. Let's call `set-results`. |
| 20/2 | 2026/10/22 11:39 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"CAN",
"CAN_RAW",
"CAN_BCM",
"CAN_ISOTP",
"CAN_GW",
"CAN_J1939",
"CAN_DEV",
"CAN_VCAN",
"CAN_VXCAN"
],
"FocusSymbols": [
"can_rx_register",
"can_rx_unregister",
"raw_bind",
"raw_release",
"raw_setsockopt",
"raw_notify",
"isotp_bind",
"isotp_release",
"isotp_notify",
"isotp_rcv",
"bcm_release",
"bcm_rx_unreg",
"can_dropped_invalid_skb",
"can_put_echo_skb"
],
"Reasoning": "The patch modifies the Linux CAN subsystem (af_can, can-raw, can-isotp, can-bcm, and can-dev). It introduces significant synchronization and locking refactorings: removing rtnl_lock from raw and isotp socket bind, release, and setsockopt; switching from a global rcvlists_lock to per-device spinlocks; introducing lockless RCU device lookups and memory barriers (smp_store_release, smp_load_acquire, smp_mb) for netdev notifier events; and handling cross-namespace device unregistration. These reachable paths are accessible via AF_CAN sockets and virtual CAN devices (vcan, vxcan), making the changes highly susceptible to race conditions, memory ordering errors, and deadlocks that warrant fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"CAN",
"CAN_RAW",
"CAN_BCM",
"CAN_ISOTP",
"CAN_GW",
"CAN_J1939",
"CAN_DEV",
"CAN_VCAN",
"CAN_VXCAN"
],
"FocusSymbols": [
"can_rx_register",
"can_rx_unregister",
"raw_bind",
"raw_release",
"raw_setsockopt",
"raw_notify",
"isotp_bind",
"isotp_release",
"isotp_notify",
"isotp_rcv",
"bcm_release",
"bcm_rx_unreg",
"can_dropped_invalid_skb",
"can_put_echo_skb"
],
"Reasoning": "The patch modifies the Linux CAN subsystem (af_can, can-raw, can-isotp, can-bcm, and can-dev). It introduces significant synchronization and locking refactorings: removing rtnl_lock from raw and isotp socket bind, release, and setsockopt; switching from a global rcvlists_lock to per-device spinlocks; introducing lockless RCU device lookups and memory barriers (smp_store_release, smp_load_acquire, smp_mb) for netdev notifier events; and handling cross-namespace device unregistration. These reachable paths are accessible via AF_CAN sockets and virtual CAN devices (vcan, vxcan), making the changes highly susceptible to race conditions, memory ordering errors, and deadlocks that warrant fuzzing.",
"WorthFuzzing": true
} |
| 21/1 | 2026/10/22 11:39 | action | If |
3mArgs:{
"WorthFuzzing": true
}Results: null |
| 22/2 | 2026/10/22 11:39 | agent | kmsan-evaluator |
3mModel:core Args: null Results: {
"KMSANReasoning": "The patch series refactors CAN networking subsystems (af_can, raw, isotp, bcm, gw, j1939):\n1. Locking \u0026 Concurrency: Replaces RTNL locking with socket locks, RCU device lookups, and per-device receive list spinlocks (dev_rcv_lists-\u003elock). Fixes concurrency races between socket operations and netdev notifier events. Potential bugs here are deadlocks, races, or use-after-free, which are covered by LOCKDEP and KASAN.\n2. Memory Freeing: Replaces kfree_skb/consume_skb with dev_kfree_skb_any/dev_consume_skb_any to ensure safe invocation from atomic or IRQ contexts.\n3. Procfs \u0026 Identifiers: Replaces kernel pointer/ident prints with socket inode numbers (sock_i_ino) and symbol formatting (%ps) in /proc/net/can/rcvlist_*. All fields printed or formatted are explicitly initialized, and all new struct fields (e.g. rcv-\u003eino, ro-\u003ebind_dev, dev_rcv_lists-\u003elock) are fully initialized upon allocation.\n4. Statistics \u0026 Checks: Replaces memset with an explicit can_reset_pkg_stats() that resets all fields of can_pkg_stats, and adds stricter CAN frame validation (can_is_can_skb / CAN_CAP_XL).\n5. setsockopt: CAN_RAW_FILTER copying is moved outside lock_sock; sfilter and dynamically allocated filters are strictly initialized before being linked or read.\n\nNone of the changes introduce risks of uninitialized memory reads, uninitialized structure padding leaks to user space, or branching on uninitialized data. A dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit b71beff35437da5a2578cb3ac13b10a616a1f59f
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 22 11:37:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/can.rst b/Documentation/networking/can.rst
index 536ff411da1d1..51bc70748f753 100644
--- a/Documentation/networking/can.rst
+++ b/Documentation/networking/can.rst
@@ -1040,17 +1040,21 @@ As described in :ref:`socketcan-receive-lists` the SocketCAN core uses several f
lists to deliver received CAN frames to CAN protocol modules. These
receive lists, their filters and the count of filter matches can be
checked in the appropriate receive list. All entries contain the
-device and a protocol module identifier::
+CAN network device, the filter (can_id/can_mask), the number of filter
+matches, the inode number of the subscribing socket and the name of the
+receive function. A sock_inode of 0 denotes a kernel internal subscriber
+without a socket (e.g. CAN gateway or J1939). When the function name can
+not be resolved (e.g. with CONFIG_KALLSYMS=n) "(unknown)" is printed::
foo@bar:~$ cat /proc/net/can/rcvlist_all
receive list 'rx_all':
- (vcan3: no entry)
- (vcan2: no entry)
- (vcan1: no entry)
- device can_id can_mask function userdata matches ident
- vcan0 000 00000000 f88e6370 f6c6f400 0 raw
(any: no entry)
+ device can_id can_mask matches sock_inode function
+ vcan0 000 00000000 0 63586 raw_rcv [can_raw]
+ (vcan1: no entry)
+ (vcan2: no entry)
+ (vcan3: no entry)
In this example an application requests any CAN traffic from vcan0::
diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
index 14edec5afb57e..738d5968dfa68 100644
--- a/drivers/net/can/dev/skb.c
+++ b/drivers/net/can/dev/skb.c
@@ -4,6 +4,7 @@
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
*/
+#include <linux/can/can-ml.h>
#include <linux/can/dev.h>
#include <linux/module.h>
#include <net/can.h>
@@ -54,6 +55,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
if (idx >= priv->echo_skb_max) {
netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
__func__, idx, priv->echo_skb_max);
+ dev_kfree_skb_any(skb);
return -EINVAL;
}
@@ -62,7 +64,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
(skb->protocol != htons(ETH_P_CAN) &&
skb->protocol != htons(ETH_P_CANFD) &&
skb->protocol != htons(ETH_P_CANXL))) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return 0;
}
@@ -90,7 +92,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
} else {
/* locking problem with netif_stop_queue() ?? */
netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return -EBUSY;
}
@@ -227,7 +229,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_cc;
}
@@ -258,7 +260,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_fd;
}
@@ -296,7 +298,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
csx = can_skb_ext_add(skb);
if (!csx) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out_error_xl;
}
@@ -388,7 +390,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
break;
case ETH_P_CANXL:
- if (!can_is_canxl_skb(skb))
+ if (!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb))
goto inval_skb;
break;
@@ -402,7 +404,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
return false;
inval_skb:
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
return true;
}
diff --git a/include/linux/can/can-ml.h b/include/linux/can/can-ml.h
index 1e99fda2b3803..cfefa4a385be9 100644
--- a/include/linux/can/can-ml.h
+++ b/include/linux/can/can-ml.h
@@ -45,6 +45,7 @@
#include <linux/can.h>
#include <linux/list.h>
#include <linux/netdevice.h>
+#include <linux/spinlock.h>
/* exposed CAN device capabilities for network layer */
#define CAN_CAP_CC BIT(0) /* CAN CC aka Classical CAN */
@@ -59,6 +60,7 @@
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
struct can_dev_rcv_lists {
+ spinlock_t lock; /* protects the receive lists and entries */
struct hlist_head rx[RX_MAX];
struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
@@ -81,6 +83,7 @@ static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)
static inline void can_set_ml_priv(struct net_device *dev,
struct can_ml_priv *ml_priv)
{
+ spin_lock_init(&ml_priv->dev_rcv_lists.lock);
netdev_set_ml_priv(dev, ml_priv, ML_PRIV_CAN);
}
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 2de74c2b78b6c..effb5c31ef401 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -51,7 +51,7 @@ extern void can_proto_unregister(const struct can_proto *cp);
int can_rx_register(struct net *net, struct net_device *dev,
canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *),
- void *data, char *ident, struct sock *sk);
+ void *data, u64 ino, struct sock *sk);
extern void can_rx_unregister(struct net *net, struct net_device *dev,
canid_t can_id, canid_t mask,
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 6d0710d6f571d..346faddee02eb 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -176,7 +176,7 @@ static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *s
return can_dropped_invalid_skb(dev, skb);
invalid_skb:
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
return true;
}
diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h
index 5d27843862fca..87ace09c0d8bd 100644
--- a/include/linux/can/skb.h
+++ b/include/linux/can/skb.h
@@ -78,12 +78,12 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
nskb = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!nskb)) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NULL;
}
can_skb_set_owner(nskb, skb->sk);
- consume_skb(skb);
+ dev_consume_skb_any(skb);
return nskb;
}
diff --git a/include/net/netns/can.h b/include/net/netns/can.h
index 48b79f7e6236d..54f23681e0d45 100644
--- a/include/net/netns/can.h
+++ b/include/net/netns/can.h
@@ -29,7 +29,7 @@ struct netns_can {
/* receive filters subscribed for 'all' CAN devices */
struct can_dev_rcv_lists *rx_alldev_list;
- spinlock_t rcvlists_lock;
+ spinlock_t rcvlists_lock; /* protects the rcv_entries(_max) stats */
struct timer_list stattimer; /* timer for statistics update */
struct can_pkg_stats *pkg_stats;
struct can_rcv_lists_stats *rcv_lists_stats;
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 34fe3b28d576f..0f3051a61aab5 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -226,7 +226,7 @@ int can_send(struct sk_buff *skb, int loop)
goto inval_skb;
}
- if (unlikely(skb->dev->type != ARPHRD_CAN)) {
+ if (unlikely(!can_get_ml_priv(skb->dev))) {
err = -EPERM;
goto inval_skb;
}
@@ -418,7 +418,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
* @mask: CAN mask (see description)
* @func: callback function on filter match
* @data: returned parameter for callback function
- * @ident: string for calling module identification
+ * @ino: inode number of sock (0 = unknown)
* @sk: socket pointer (might be NULL)
*
* Description:
@@ -443,7 +443,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
*/
int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
canid_t mask, void (*func)(struct sk_buff *, void *),
- void *data, char *ident, struct sock *sk)
+ void *data, u64 ino, struct sock *sk)
{
struct receiver *rcv;
struct hlist_head *rcv_list;
@@ -452,7 +452,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
/* insert new receiver (dev,canid,mask) -> (func,data) */
- if (dev && (dev->type != ARPHRD_CAN || !can_get_ml_priv(dev)))
+ if (dev && !can_get_ml_priv(dev))
return -ENODEV;
if (dev && !net_eq(net, dev_net(dev)))
@@ -462,9 +462,10 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
if (!rcv)
return -ENOMEM;
- spin_lock_bh(&net->can.rcvlists_lock);
-
dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
+
+ spin_lock_bh(&dev_rcv_lists->lock);
+
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
rcv->can_id = can_id;
@@ -472,12 +473,15 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
atomic_long_set(&rcv->matches, 0);
rcv->func = func;
rcv->data = data;
- rcv->ident = ident;
+ rcv->ino = ino;
rcv->sk = sk;
hlist_add_head_rcu(&rcv->list, rcv_list);
dev_rcv_lists->entries++;
+ spin_unlock_bh(&dev_rcv_lists->lock);
+
+ spin_lock_bh(&net->can.rcvlists_lock);
rcv_lists_stats->rcv_entries++;
rcv_lists_stats->rcv_entries_max = max(rcv_lists_stats->rcv_entries_max,
rcv_lists_stats->rcv_entries);
@@ -509,6 +513,14 @@ static void can_rx_delete_receiver(struct rcu_head *rp)
*
* Description:
* Removes subscription entry depending on given (subscription) values.
+ * The receive lists of a CAN device are protected by a per-device lock,
+ * so the subscription is found even when the device has been moved to
+ * another network namespace after can_rx_register(). The caller has to
+ * ensure that @dev stays valid, e.g. by holding a reference to @dev or
+ * RTNL. @net should be the namespace that has been used for
+ * can_rx_register(). It selects the 'any' CAN device lists for
+ * @dev == NULL and is otherwise only used for the receive list
+ * statistics, which become imprecise when another namespace is passed.
*/
void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
canid_t mask, void (*func)(struct sk_buff *, void *),
@@ -519,15 +531,13 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
struct can_dev_rcv_lists *dev_rcv_lists;
- if (dev && dev->type != ARPHRD_CAN)
+ if (dev && !can_get_ml_priv(dev))
return;
- if (dev && !net_eq(net, dev_net(dev)))
- return;
+ dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
- spin_lock_bh(&net->can.rcvlists_lock);
+ spin_lock_bh(&dev_rcv_lists->lock);
- dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
/* Search the receiver list for the item to delete. This should
@@ -547,26 +557,26 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
* a warning here.
*/
if (!rcv) {
+ spin_unlock_bh(&dev_rcv_lists->lock);
pr_warn("can: receive list entry not found for dev %s, id %03X, mask %03X\n",
DNAME(dev), can_id, mask);
- goto out;
+ return;
}
hlist_del_rcu(&rcv->list);
dev_rcv_lists->entries--;
+ spin_unlock_bh(&dev_rcv_lists->lock);
+
+ spin_lock_bh(&net->can.rcvlists_lock);
if (rcv_lists_stats->rcv_entries > 0)
rcv_lists_stats->rcv_entries--;
-
- out:
spin_unlock_bh(&net->can.rcvlists_lock);
/* schedule the receiver item for deletion */
- if (rcv) {
- if (rcv->sk)
- sock_hold(rcv->sk);
- call_rcu(&rcv->rcu, can_rx_delete_receiver);
- }
+ if (rcv->sk)
+ sock_hold(rcv->sk);
+ call_rcu(&rcv->rcu, can_rx_delete_receiver);
}
EXPORT_SYMBOL(can_rx_unregister);
@@ -684,8 +694,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_can_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -710,8 +719,7 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_canfd_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -736,8 +744,7 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct can_skb_ext *csx = can_skb_ext_find(skb);
- if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
- !csx || !can_is_canxl_skb(skb))) {
+ if (unlikely(!can_get_ml_priv(dev) || !csx || !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);
@@ -826,6 +833,7 @@ static int can_pernet_init(struct net *net)
net->can.rx_alldev_list = kzalloc_obj(*net->can.rx_alldev_list);
if (!net->can.rx_alldev_list)
goto out;
+ spin_lock_init(&net->can.rx_alldev_list->lock);
net->can.pkg_stats = kzalloc_obj(*net->can.pkg_stats);
if (!net->can.pkg_stats)
goto out_free_rx_alldev_list;
diff --git a/net/can/af_can.h b/net/can/af_can.h
index 87887014f5628..6e593ed5db5f0 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -55,7 +55,7 @@ struct receiver {
atomic_long_t matches;
void (*func)(struct sk_buff *skb, void *data);
void *data;
- char *ident;
+ u64 ino;
struct sock *sk;
struct rcu_head rcu;
};
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3d637a1e0ac1a..7bd2aa39777af 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -54,6 +54,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
@@ -220,9 +221,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
- seq_printf(m, ">>> socket %pK", sk->sk_socket);
- seq_printf(m, " / sk %pK", sk);
- seq_printf(m, " / bo %pK", bo);
+ seq_printf(m, ">>> sock inode %s", bo->procname);
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
seq_printf(m, " <<<\n");
@@ -963,7 +962,7 @@ static void bcm_remove_op(struct bcm_op *op)
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{
if (op->rx_reg_dev == dev) {
- can_rx_unregister(dev_net(dev), dev, op->can_id,
+ can_rx_unregister(sock_net(op->sk), dev, op->can_id,
REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */
@@ -989,21 +988,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
/* disable automatic timer on frame reception */
op->flags |= RX_NO_AUTOTIMER;
- /*
- * Don't care if we're bound or not (due to netdev
- * problems) can_rx_unregister() is always a save
- * thing to do here.
- */
if (op->ifindex) {
- /*
- * Only remove subscriptions that had not
- * been removed due to NETDEV_UNREGISTER
- * in bcm_notifier()
- *
- * op->rx_reg_dev is a tracked reference taken
- * when the subscription was registered, so it
- * stays valid here even if a concurrent
- * NETDEV_UNREGISTER already unlisted the dev.
+ /* Only remove subscriptions that had not been
+ * removed in bcm_notify(). op->rx_reg_dev keeps
+ * the device's receive lists accessible.
*/
if (op->rx_reg_dev)
bcm_rx_unreg(op->rx_reg_dev, op);
@@ -1536,7 +1524,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
- "bcm", sk);
+ sock_i_ino(sk), sk);
/* keep a tracked reference so that a later
* unregister can safely reach the device even
@@ -1560,7 +1548,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
} else {
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
- bcm_rx_handler, op, "bcm", sk);
+ bcm_rx_handler, op,
+ sock_i_ino(sk), sk);
}
if (err) {
@@ -1719,7 +1708,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
goto out_release;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
dev_put(dev);
ret = -ENODEV;
goto out_release;
@@ -1867,7 +1856,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
@@ -1935,6 +1924,7 @@ static int bcm_release(struct socket *sock)
list_del(&bo->notifier);
spin_unlock(&bcm_notifier_lock);
+ /* bcm_notify() can not access this socket anymore */
lock_sock(sk);
#if IS_ENABLED(CONFIG_PROC_FS)
@@ -1948,29 +1938,20 @@ static int bcm_release(struct socket *sock)
bcm_remove_op(op);
}
- list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
- /*
- * Don't care if we're bound or not (due to netdev problems)
- * can_rx_unregister() is always a save thing to do here.
- */
+ list_for_each_entry(op, &bo->rx_ops, list) {
if (op->ifindex) {
- /*
- * Only remove subscriptions that had not
- * been removed due to NETDEV_UNREGISTER
- * in bcm_notifier()
- *
- * op->rx_reg_dev is a tracked reference taken
- * when the subscription was registered, so it
- * stays valid here even if a concurrent
- * NETDEV_UNREGISTER already unlisted the device.
+ /* Only remove subscriptions that had not been removed
+ * in bcm_notify(). op->rx_reg_dev keeps the device's
+ * receive lists accessible.
*/
if (op->rx_reg_dev)
bcm_rx_unreg(op->rx_reg_dev, op);
- } else
+ } else {
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
-
+ }
+ cond_resched();
}
synchronize_rcu();
@@ -2024,7 +2005,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
ret = -ENODEV;
goto fail;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
dev_put(dev);
ret = -ENODEV;
goto fail;
@@ -2040,8 +2021,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
#if IS_ENABLED(CONFIG_PROC_FS)
if (net->can.bcmproc_dir) {
- /* unique socket address as filename */
- sprintf(bo->procname, "%llu", sock_i_ino(sk));
+ /* use unique socket inode number as filename */
+ snprintf(bo->procname, sizeof(bo->procname),
+ "%llu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
net->can.bcmproc_dir,
bcm_proc_show, sk);
diff --git a/net/can/gw.c b/net/can/gw.c
index 5793cb2420ed3..8a5327d8a00e8 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -52,6 +52,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/gw.h>
@@ -579,7 +580,7 @@ static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
{
return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
- gwj, "gw", NULL);
+ gwj, 0, NULL);
}
static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
@@ -612,7 +613,7 @@ static int cgw_notifier(struct notifier_block *nb,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg == NETDEV_UNREGISTER) {
@@ -1163,7 +1164,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!gwj->src.dev)
goto out;
- if (gwj->src.dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(gwj->src.dev))
goto out;
gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
@@ -1171,7 +1172,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!gwj->dst.dev)
goto out;
- if (gwj->dst.dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(gwj->dst.dev))
goto out;
/* is sending the skb back to the incoming interface intended? */
diff --git a/net/can/isotp.c b/net/can/isotp.c
index f5dc9d04bd686..9386389cd702e 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -65,6 +65,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/isotp.h>
@@ -755,6 +756,14 @@ 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 */
@@ -1488,11 +1497,11 @@ static int isotp_release(struct socket *sock)
list_del(&so->notifier);
spin_unlock(&isotp_notifier_lock);
- rtnl_lock();
lock_sock(sk);
/* remove current filters & unregister
- * tracked reference so->dev is taken at bind() time with rtnl_lock
+ * the tracked reference so->dev keeps the device's receive lists
+ * accessible, even after a namespace change
*/
if (so->bound && so->dev) {
if (isotp_register_rxid(so))
@@ -1510,8 +1519,6 @@ static int isotp_release(struct socket *sock)
so->bound = 0;
so->dev = NULL;
- rtnl_unlock();
-
/* Always wait for a grace period before touching the timers below.
* A concurrent NETDEV_UNREGISTER may have already unregistered our
* filters and cleared so->bound in isotp_notify() without waiting
@@ -1579,7 +1586,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
if (!addr->can_ifindex)
return -ENODEV;
- rtnl_lock();
+ /* lock_sock() serializes the binding with isotp_notify() */
lock_sock(sk);
if (so->bound) {
@@ -1605,34 +1612,44 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
goto out;
}
- dev = dev_get_by_index(net, addr->can_ifindex);
- if (!dev) {
- err = -ENODEV;
- goto out;
- }
- if (dev->type != ARPHRD_CAN) {
+ /* Publish the device inside the RCU section of the lookup: When it
+ * is unlisted concurrently, either isotp_notify() sees so->dev after
+ * synchronize_net() or the lookup fails. Check the device before, so
+ * that bind() can not fail after so->dev has been published.
+ */
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(net, addr->can_ifindex);
+ if (!dev || !can_get_ml_priv(dev))
err = -ENODEV;
- goto out_put_dev;
- }
- if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+ else if (READ_ONCE(dev->mtu) < so->ll.mtu)
err = -EINVAL;
- goto out_put_dev;
+ if (!err) {
+ dev_hold(dev);
+ WRITE_ONCE(so->dev, dev);
}
- if (!(dev->flags & IFF_UP))
+ rcu_read_unlock();
+ if (err)
+ goto out;
+
+ /* Either isotp_notify() sees so->dev on NETDEV_DOWN or we see the
+ * cleared IFF_UP, pairs with isotp_notify()
+ */
+ smp_mb();
+ if (!(READ_ONCE(dev->flags) & IFF_UP))
notify_enetdown = 1;
ifindex = dev->ifindex;
if (isotp_register_rxid(so))
can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
- isotp_rcv, sk, "isotp", sk);
+ isotp_rcv, sk, sock_i_ino(sk), sk);
/* no consecutive frame echo skb in flight */
WRITE_ONCE(so->cfecho, 0);
/* register for echo skb's */
can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
- isotp_rcv_echo, sk, "isotpe", sk);
+ isotp_rcv_echo, sk, sock_i_ino(sk), sk);
/* switch to new settings */
so->ifindex = ifindex;
@@ -1644,15 +1661,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
* can safely reach the device later, even if a concurrent
* NETDEV_UNREGISTER has already unlisted it by ifindex.
*/
- so->dev = dev;
netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL);
-out_put_dev:
- /* remove potential reference from dev_get_by_index() */
+ /* remove reference from the device lookup */
dev_put(dev);
out:
release_sock(sk);
- rtnl_unlock();
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
@@ -1855,12 +1869,21 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
- if (so->dev != dev)
+ if (msg == NETDEV_DOWN)
+ smp_mb(); /* pairs with isotp_bind(), see raw_notify() */
+
+ /* lockless fast path, see raw_notify() */
+ if (READ_ONCE(so->dev) != dev)
return;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
+ if (so->dev != dev) {
+ release_sock(sk);
+ break;
+ }
+
/* remove current filters & unregister */
if (so->bound) {
if (isotp_register_rxid(so))
@@ -1876,7 +1899,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
so->ifindex = 0;
so->bound = 0;
- so->dev = NULL;
+ WRITE_ONCE(so->dev, NULL);
release_sock(sk);
sk->sk_err = ENODEV;
@@ -1897,7 +1920,7 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
index 5e5e6c228f225..d9384cf0e356f 100644
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -184,7 +184,7 @@ static int j1939_can_rx_register(struct j1939_priv *priv)
j1939_priv_get(priv);
ret = can_rx_register(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,
- j1939_can_recv, priv, "j1939", NULL);
+ j1939_can_recv, priv, 0, NULL);
if (ret < 0) {
j1939_priv_put(priv);
return ret;
diff --git a/net/can/proc.c b/net/can/proc.c
index de4d05ae34597..b7496d2a37cd1 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -76,16 +76,39 @@ static const char rx_list_name[][8] = {
* af_can statistics stuff
*/
+static void can_reset_pkg_stats(struct can_pkg_stats *pkg_stats)
+{
+ atomic_long_set(&pkg_stats->rx_frames, 0);
+ atomic_long_set(&pkg_stats->tx_frames, 0);
+ atomic_long_set(&pkg_stats->matches, 0);
+
+ pkg_stats->total_rx_rate = 0;
+ pkg_stats->total_tx_rate = 0;
+ pkg_stats->total_rx_match_ratio = 0;
+
+ pkg_stats->current_rx_rate = 0;
+ pkg_stats->current_tx_rate = 0;
+ pkg_stats->current_rx_match_ratio = 0;
+
+ pkg_stats->max_rx_rate = 0;
+ pkg_stats->max_tx_rate = 0;
+ pkg_stats->max_rx_match_ratio = 0;
+
+ atomic_long_set(&pkg_stats->rx_frames_delta, 0);
+ atomic_long_set(&pkg_stats->tx_frames_delta, 0);
+ atomic_long_set(&pkg_stats->matches_delta, 0);
+}
+
static void can_init_stats(struct net *net)
{
struct can_pkg_stats *pkg_stats = net->can.pkg_stats;
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
/*
- * This memset function is called from a timer context (when
+ * This stats reset is called from a timer context (when
* can_stattimer is active which is the default) OR in a process
* context (reading the proc_fs when can_stattimer is disabled).
*/
- memset(pkg_stats, 0, sizeof(struct can_pkg_stats));
+ can_reset_pkg_stats(pkg_stats);
pkg_stats->jiffies_init = jiffies;
rcv_lists_stats->stats_reset++;
@@ -121,29 +144,23 @@ void can_stat_update(struct timer_list *t)
long rx_frames = atomic_long_read(&pkg_stats->rx_frames);
long tx_frames = atomic_long_read(&pkg_stats->tx_frames);
long matches = atomic_long_read(&pkg_stats->matches);
- long rx_frames_delta = atomic_long_read(&pkg_stats->rx_frames_delta);
- long tx_frames_delta = atomic_long_read(&pkg_stats->tx_frames_delta);
- long matches_delta = atomic_long_read(&pkg_stats->matches_delta);
+ /* fetch and clear values for 'current rate' calculation */
+ long rx_frames_delta = atomic_long_xchg(&pkg_stats->rx_frames_delta, 0);
+ long tx_frames_delta = atomic_long_xchg(&pkg_stats->tx_frames_delta, 0);
+ long matches_delta = atomic_long_xchg(&pkg_stats->matches_delta, 0);
- /* restart counting in timer context on user request */
- if (user_reset)
- can_init_stats(net);
-
- /* restart counting on jiffies overflow */
- if (j < pkg_stats->jiffies_init)
- can_init_stats(net);
-
- /* prevent overflow in calc_rate() */
- if (rx_frames > (LONG_MAX / HZ))
- can_init_stats(net);
-
- /* prevent overflow in calc_rate() */
- if (tx_frames > (LONG_MAX / HZ))
- can_init_stats(net);
-
- /* matches overflow - very improbable */
- if (matches > (LONG_MAX / 100))
+ /*
+ * Restart counting in timer context on user request, on jiffies
+ * overflow or to prevent overflows in calc_rate() and in the
+ * match ratio calculation. The local snapshots are stale after
+ * the reset, so skip the calculation for this interval.
+ */
+ if (user_reset || j < pkg_stats->jiffies_init ||
+ rx_frames > (LONG_MAX / HZ) || tx_frames > (LONG_MAX / HZ) ||
+ matches > (LONG_MAX / 100)) {
can_init_stats(net);
+ goto out;
+ }
/* calc total values */
if (rx_frames)
@@ -172,11 +189,7 @@ void can_stat_update(struct timer_list *t)
if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio)
pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio;
- /* clear values for 'current rate' calculation */
- atomic_long_set(&pkg_stats->tx_frames_delta, 0);
- atomic_long_set(&pkg_stats->rx_frames_delta, 0);
- atomic_long_set(&pkg_stats->matches_delta, 0);
-
+out:
/* restart timer (one second) */
mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ));
}
@@ -191,26 +204,32 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
struct receiver *r;
hlist_for_each_entry_rcu(r, rx_list, list) {
+ char symcheck[4];
char *fmt = (r->can_id & CAN_EFF_FLAG)?
- " %-5s %08x %08x %pK %pK %8ld %s\n" :
- " %-5s %03x %08x %pK %pK %8ld %s\n";
+ " %6s %08x %08x %8ld %10llu" :
+ " %6s %03x %08x %8ld %10llu";
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
- r->func, r->data, atomic_long_read(&r->matches),
- r->ident);
+ atomic_long_read(&r->matches), r->ino);
+
+ /* %ps falls back to the raw text address when the symbol
+ * can not be resolved (KALLSYMS=n, stripped module symbols)
+ */
+ snprintf(symcheck, sizeof(symcheck), "%ps", r->func);
+ if (strncmp(symcheck, "0x", 2))
+ seq_printf(m, " %ps\n", r->func);
+ else
+ seq_puts(m, " (unknown)\n");
}
}
static void can_print_recv_banner(struct seq_file *m)
{
/*
- * can1. 00000000 00000000 00000000
- * ....... 0 tp20
+ * device can_id can_mask matches sock_inode function
+ * vcan0 000 00000000 0 43798 raw_rcv [can_raw]
*/
- if (IS_ENABLED(CONFIG_64BIT))
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
- else
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
+ seq_puts(m, " device can_id can_mask matches sock_inode function\n");
}
static int can_stats_proc_show(struct seq_file *m, void *v)
diff --git a/net/can/raw.c b/net/can/raw.c
index 0acd4f6c6dd67..d07f3bb68cd5c 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -85,6 +85,7 @@ struct raw_sock {
struct sock sk;
struct net_device *dev;
netdevice_tracker dev_tracker;
+ struct net_device *bind_dev; /* hint for raw_notify(), no reference */
struct list_head notifier;
int ifindex;
unsigned int bound:1;
@@ -224,9 +225,10 @@ static int raw_enable_filters(struct net *net, struct net_device *dev,
int i;
for (i = 0; i < count; i++) {
+ cond_resched();
err = can_rx_register(net, dev, filter[i].can_id,
filter[i].can_mask,
- raw_rcv, sk, "raw", sk);
+ raw_rcv, sk, sock_i_ino(sk), sk);
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
@@ -247,7 +249,7 @@ static int raw_enable_errfilter(struct net *net, struct net_device *dev,
if (err_mask)
err = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,
- raw_rcv, sk, "raw", sk);
+ raw_rcv, sk, sock_i_ino(sk), sk);
return err;
}
@@ -258,9 +260,11 @@ static void raw_disable_filters(struct net *net, struct net_device *dev,
{
int i;
- for (i = 0; i < count; i++)
+ for (i = 0; i < count; i++) {
can_rx_unregister(net, dev, filter[i].can_id,
filter[i].can_mask, raw_rcv, sk);
+ cond_resched();
+ }
}
static inline void raw_disable_errfilter(struct net *net,
@@ -305,16 +309,31 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
struct net_device *dev)
{
struct sock *sk = &ro->sk;
+ struct net_device *bind_dev;
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
- if (ro->dev != dev)
+ if (msg == NETDEV_DOWN)
+ smp_mb(); /* IFF_UP is cleared before, pairs with raw_bind() */
+
+ /* Lockless fast path, ro->bind_dev covers a concurrent bind().
+ * Read it before ro->dev, pairs with smp_store_release() in
+ * raw_bind(). A stale ro->bind_dev only costs a lock_sock().
+ */
+ bind_dev = smp_load_acquire(&ro->bind_dev);
+ if (READ_ONCE(ro->dev) != dev &&
+ (msg != NETDEV_UNREGISTER || bind_dev != dev))
return;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
+ if (ro->dev != dev) {
+ release_sock(sk);
+ break;
+ }
+
/* remove current filters & unregister */
if (ro->bound) {
raw_disable_allfilters(dev_net(dev), dev, sk);
@@ -326,7 +345,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
ro->ifindex = 0;
ro->bound = 0;
- ro->dev = NULL;
+ WRITE_ONCE(ro->dev, NULL);
ro->count = 0;
release_sock(sk);
@@ -348,7 +367,7 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- if (dev->type != ARPHRD_CAN)
+ if (!can_get_ml_priv(dev))
return NOTIFY_DONE;
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
return NOTIFY_DONE;
@@ -381,6 +400,7 @@ static int raw_init(struct sock *sk)
ro->bound = 0;
ro->ifindex = 0;
ro->dev = NULL;
+ ro->bind_dev = NULL;
/* set default filter to single entry dfilter */
ro->dfilter.can_id = 0;
@@ -431,13 +451,15 @@ static int raw_release(struct socket *sock)
list_del(&ro->notifier);
spin_unlock(&raw_notifier_lock);
- rtnl_lock();
lock_sock(sk);
- /* remove current filters & unregister */
+ /* remove current filters & unregister
+ * the tracked reference ro->dev keeps the device's receive lists
+ * accessible, even after a namespace change
+ */
if (ro->bound) {
if (ro->dev) {
- raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
+ raw_disable_allfilters(net, ro->dev, sk);
netdev_put(ro->dev, &ro->dev_tracker);
} else {
raw_disable_allfilters(net, NULL, sk);
@@ -456,7 +478,6 @@ static int raw_release(struct socket *sock)
sock->sk = NULL;
release_sock(sk);
- rtnl_unlock();
sock_prot_inuse_add(net, sk->sk_prot, -1);
sock_put(sk);
@@ -479,26 +500,34 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
if (addr->can_family != AF_CAN)
return -EINVAL;
- rtnl_lock();
+ /* lock_sock() serializes the binding with raw_notify() */
lock_sock(sk);
if (ro->bound && addr->can_ifindex == ro->ifindex)
goto out;
if (addr->can_ifindex) {
- dev = dev_get_by_index(sock_net(sk), addr->can_ifindex);
+ /* Publish the device inside the RCU section of the lookup:
+ * When it is unlisted concurrently, either raw_notify() sees
+ * ro->bind_dev after synchronize_net() or the lookup fails.
+ */
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(sock_net(sk), addr->can_ifindex);
+ if (dev) {
+ dev_hold(dev);
+ /* pairs with smp_load_acquire() in raw_notify() */
+ smp_store_release(&ro->bind_dev, dev);
+ }
+ rcu_read_unlock();
if (!dev) {
err = -ENODEV;
goto out;
}
- if (dev->type != ARPHRD_CAN) {
+ if (!can_get_ml_priv(dev)) {
err = -ENODEV;
goto out_put_dev;
}
- if (!(dev->flags & IFF_UP))
- notify_enetdown = 1;
-
ifindex = dev->ifindex;
/* filters set by default/setsockopt */
@@ -517,7 +546,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
if (ro->bound) {
/* unregister old filters */
if (ro->dev) {
- raw_disable_allfilters(dev_net(ro->dev),
+ raw_disable_allfilters(sock_net(sk),
ro->dev, sk);
/* drop reference to old ro->dev */
netdev_put(ro->dev, &ro->dev_tracker);
@@ -528,17 +557,24 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
ro->ifindex = ifindex;
ro->bound = 1;
/* bind() ok -> hold a reference for new ro->dev */
- ro->dev = dev;
- if (ro->dev)
- netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
+ WRITE_ONCE(ro->dev, dev);
+ netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
+
+ if (dev) {
+ /* Either raw_notify() sees ro->dev on NETDEV_DOWN or
+ * we see the cleared IFF_UP, pairs with raw_notify()
+ */
+ smp_mb();
+ if (!(READ_ONCE(dev->flags) & IFF_UP))
+ notify_enetdown = 1;
+ }
}
out_put_dev:
- /* remove potential reference from dev_get_by_index() */
+ /* remove reference from the device lookup */
dev_put(dev);
out:
release_sock(sk);
- rtnl_unlock();
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
@@ -567,58 +603,41 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
}
static int raw_setsockopt_locked(struct socket *sock, int optname,
- sockptr_t optval, unsigned int optlen)
+ sockptr_t optval, unsigned int optlen,
+ struct can_filter *filter,
+ struct can_filter *sfilter, int count)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
- struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
- struct can_filter sfilter; /* single filter */
struct net_device *dev = NULL;
can_err_mask_t err_mask = 0;
- int count = 0;
int flag;
int err = 0;
switch (optname) {
case CAN_RAW_FILTER:
- if (optlen % sizeof(struct can_filter) != 0)
- return -EINVAL;
-
- if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
- return -EINVAL;
-
- count = optlen / sizeof(struct can_filter);
-
- if (count > 1) {
- /* filter does not fit into dfilter => alloc space */
- filter = memdup_sockptr(optval, optlen);
- if (IS_ERR(filter))
- return PTR_ERR(filter);
- } else if (count == 1) {
- if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
- return -EFAULT;
- }
-
+ /* @filter has been copied by raw_setsockopt(). It is linked
+ * to the socket on success and freed by the caller on error.
+ *
+ * The reg_state check is only a shortcut: On a concurrent
+ * unregistration or namespace change raw_notify() waits for
+ * lock_sock() and removes the filters set here.
+ */
dev = ro->dev;
- if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
- if (count > 1)
- kfree(filter);
+ if (ro->bound && dev &&
+ READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
return -ENODEV;
- }
if (ro->bound) {
/* (try to) register the new filters */
if (count == 1)
err = raw_enable_filters(sock_net(sk), dev, sk,
- &sfilter, 1);
+ sfilter, 1);
else
err = raw_enable_filters(sock_net(sk), dev, sk,
filter, count);
- if (err) {
- if (count > 1)
- kfree(filter);
+ if (err)
return err;
- }
/* remove old filter registrations */
raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
@@ -632,7 +651,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
/* link new filters to the socket */
if (count == 1) {
/* copy filter data for single filter */
- ro->dfilter = sfilter;
+ ro->dfilter = *sfilter;
filter = &ro->dfilter;
}
ro->filter = filter;
@@ -648,8 +667,10 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
err_mask &= CAN_ERR_MASK;
+ /* lockless shortcut, see CAN_RAW_FILTER */
dev = ro->dev;
- if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
+ if (ro->bound && dev &&
+ READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
return -ENODEV;
/* remove current error mask */
@@ -751,18 +772,47 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
+ struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
+ struct can_filter sfilter; /* single filter */
+ int count = 0;
int err;
if (level != SOL_CAN_RAW)
return -EINVAL;
- rtnl_lock();
+ if (optname == CAN_RAW_FILTER) {
+ /* Copy the filters before lock_sock(), so a stalled copy can
+ * not block raw_notify() waiting for lock_sock() under RTNL.
+ */
+ if (optlen % sizeof(struct can_filter) != 0)
+ return -EINVAL;
+
+ if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
+ return -EINVAL;
+
+ count = optlen / sizeof(struct can_filter);
+
+ if (count > 1) {
+ /* filter does not fit into dfilter => alloc space */
+ filter = memdup_sockptr(optval, optlen);
+ if (IS_ERR(filter))
+ return PTR_ERR(filter);
+ } else if (count == 1) {
+ if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
+ return -EFAULT;
+ }
+ }
+
lock_sock(sk);
- err = raw_setsockopt_locked(sock, optname, optval, optlen);
+ err = raw_setsockopt_locked(sock, optname, optval, optlen,
+ filter, &sfilter, count);
release_sock(sk);
- rtnl_unlock();
+
+ /* on error free the filters allocated for CAN_RAW_FILTER above */
+ if (err && count > 1)
+ kfree(filter);
return err;
}
|
| 23/3 | 2026/10/22 11:39 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19843 output: 30 thoughts: 3541 Thoughts: **KMSAN Evaluation: CAN Subsystem Patch Series** Okay, let's break this down methodically. My primary goal here is to determine if this CAN subsystem patch series introduces any risks that warrant a dedicated KMSAN fuzzing session. The key is to avoid wasting resources; if KASAN, LOCKDEP, or standard bug detectors would catch any potential issues, we skip KMSAN. KMSAN is only needed for uninitialized memory risks *that are unique to KMSAN*. First, I meticulously reviewed the instructions and criteria. The main focus is on whether the patch introduces or modifies code that interacts with kernel structures being passed to user space, depends on potentially uninitialized variables, involves complex data structure allocation with later reads, or alters bounds checks in a way that risks reading uninitialized bytes. With that in mind, I dove into the diff. The initial doc changes for `/proc/net/can/rcvlist_all` are purely informational, no KMSAN risk there. The code changes involving `dev_kfree_skb_any` are mostly just refactoring and shouldn't introduce any uninitialized memory concerns, given how the SKBs are initialized before use and freed. I also checked for changes in the `can_dropped_invalid_skb` function, but it only contains additional checks, with no uninitialized memory risks. The changes in `include/linux/can/can-ml.h` adding a spinlock to `struct can_dev_rcv_lists` and initializing it, are safe. The changes to `include/linux/can/core.h` and `include/linux/can/dev.h` also seem to be renames or refactorings that don't increase risk. Ditto for the comments updates in `include/net/netns/can.h`. Moving into `net/can/af_can.c`, the changes appear to center around using per-device locking for receiver lists and refactoring of `can_rx_register`/`unregister`. The replacement of `char *ident` with `u64 ino` in the `receiver` struct and related changes are fine. The changes in `net/can/af_can.h` don't seem like a problem. In `net/can/bcm.c`, there are many replacements of `dev->type != ARPHRD_CAN` with `!can_get_ml_priv(dev)`, which appears to be a cleaner check. Also, using `sock_i_ino(sk)` and `snprintf`, and calling `can_rx_unregister` look like cleanups, and no obvious KMSAN risks are immediately apparent. The changes in `net/can/gw.c` are minor renames and the `0` argument shouldn't introduce any new risks. In `net/can/isotp.c`, the new frame content checks look safe, and the lock changes don't seem to be an issue. The change in `net/can/j1939/main.c` is just passing a `0`, so no problem. `net/can/proc.c` warrants careful inspection. The `can_reset_pkg_stats` changes are related to a struct already zeroed at allocation, and the atomic updates are safe. The `can_print_rcvlist` change involving the `symcheck` buffer and `strncmp` is interesting. The code uses `snprintf`, which guarantees NUL-termination, and then `strncmp` to check the beginning of the buffer. Therefore, there's no risk of reading uninitialized memory. Also, nothing is leaked to user. The proc recv banner is just text. Finally, in `net/can/raw.c`, the introduction of `bind_dev` and the changes in `raw_setsockopt` and `raw_getsockopt` require very close scrutiny. It looks like the original code copies socket filters from user, which means that any risk of uninitialized memory reads happens *after* the potentially uninitialized parts of the struct are copied. I checked that `sfilter` is only read when it has been initialized by copying from userspace. `filter` is also fine. I also confirmed with `raw_getsockopt` that nothing is being copied back to user space. After this complete review, I'm confident that these patches do *not* introduce any new or significant uninitialized memory risks that KMSAN would detect but that are missed by KASAN, LOCKDEP, or the standard bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified for this patch series. |
| 24/3 | 2026/10/22 11:39 | tool | grepper |
0mArgs:{
"Expression": "raw_getsockopt",
"PathPrefixes": [
"net/can/"
]
}Results: {
"Output": "net/can/raw.c=771=static int raw_setsockopt(struct socket *sock, int level, int optname,\n--\nnet/can/raw.c-819-\nnet/can/raw.c:820:static int raw_getsockopt(struct socket *sock, int level, int optname,\nnet/can/raw.c-821-\t\t\t sockopt_t *opt)\n--\nnet/can/raw.c=1122=static const struct proto_ops raw_ops = {\n--\nnet/can/raw.c-1135-\t.setsockopt = raw_setsockopt,\nnet/can/raw.c:1136:\t.getsockopt_iter = raw_getsockopt,\nnet/can/raw.c-1137-\t.sendmsg = raw_sendmsg,\n"
} |
| 25/3 | 2026/10/22 11:39 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24617 output: 35 thoughts: 21 Thoughts: Okay, let's dive into this. Here's my take on examining that code: **Analyzing `raw_getsockopt` in `net/can/raw.c`** Alright, I'm heading into the `net/can/raw.c` file to check out the `raw_getsockopt` function. My mental checklist kicks in automatically: I'm expecting this to be the function handling the retrieval of CAN raw socket options. I'll need to pay close attention to which options are supported, how the kernel handles the retrieval of those options, and any potential error handling involved. I'm immediately looking for any `copy_to_user` calls, and wondering about how data is marshaled back to the userspace application. Specifically, I'm looking at the arguments it accepts ( `struct sock *sk, int level, int optname, char __user *optval, int __user *optlen` ), and immediately start thinking about potential vulnerabilities. Buffer overflows are always a concern in `getsockopt` implementations, so I'll be keeping a sharp eye on how `optlen` is used. I'll also be considering whether appropriate locking is in place to protect against race conditions, especially if there are any shared data structures involved. I'm expecting to see a switch statement handling different `optname` values, likely each relating to a specific CAN raw socket configuration parameter. I'm going to quickly get a sense of how complex this is, and begin forming an idea of whether a potential bug exists. |
| 26/3 | 2026/10/22 11:39 | tool | read-file |
0mArgs:{
"File": "net/can/raw.c",
"FirstLine": 820,
"LineCount": 60
}Results: {
"Contents": " 820:\tstatic int raw_getsockopt(struct socket *sock, int level, int optname,\n 821:\t\t\t\t sockopt_t *opt)\n 822:\t{\n 823:\t\tstruct sock *sk = sock-\u003esk;\n 824:\t\tstruct raw_sock *ro = raw_sk(sk);\n 825:\t\tint flag;\n 826:\t\tint len;\n 827:\t\tvoid *val;\n 828:\t\n 829:\t\tif (level != SOL_CAN_RAW)\n 830:\t\t\treturn -EINVAL;\n 831:\t\tlen = opt-\u003eoptlen;\n 832:\t\tif (len \u003c 0)\n 833:\t\t\treturn -EINVAL;\n 834:\t\n 835:\t\tswitch (optname) {\n 836:\t\tcase CAN_RAW_FILTER: {\n 837:\t\t\tint err = 0;\n 838:\t\n 839:\t\t\tlock_sock(sk);\n 840:\t\t\tif (ro-\u003ecount \u003e 0) {\n 841:\t\t\t\tint fsize = ro-\u003ecount * sizeof(struct can_filter);\n 842:\t\n 843:\t\t\t\t/* user space buffer to small for filter list? */\n 844:\t\t\t\tif (len \u003c fsize) {\n 845:\t\t\t\t\t/* return -ERANGE and needed space in optlen */\n 846:\t\t\t\t\terr = -ERANGE;\n 847:\t\t\t\t\topt-\u003eoptlen = fsize;\n 848:\t\t\t\t} else {\n 849:\t\t\t\t\tif (len \u003e fsize)\n 850:\t\t\t\t\t\tlen = fsize;\n 851:\t\t\t\t\tif (copy_to_iter(ro-\u003efilter, len,\n 852:\t\t\t\t\t\t\t \u0026opt-\u003eiter_out) != len)\n 853:\t\t\t\t\t\terr = -EFAULT;\n 854:\t\t\t\t}\n 855:\t\t\t} else {\n 856:\t\t\t\tlen = 0;\n 857:\t\t\t}\n 858:\t\t\trelease_sock(sk);\n 859:\t\n 860:\t\t\tif (!err)\n 861:\t\t\t\topt-\u003eoptlen = len;\n 862:\t\t\treturn err;\n 863:\t\t}\n 864:\t\tcase CAN_RAW_ERR_FILTER:\n 865:\t\t\tif (len \u003e sizeof(can_err_mask_t))\n 866:\t\t\t\tlen = sizeof(can_err_mask_t);\n 867:\t\t\tval = \u0026ro-\u003eerr_mask;\n 868:\t\t\tbreak;\n 869:\t\n 870:\t\tcase CAN_RAW_LOOPBACK:\n 871:\t\t\tif (len \u003e sizeof(int))\n 872:\t\t\t\tlen = sizeof(int);\n 873:\t\t\tflag = ro-\u003eloopback;\n 874:\t\t\tval = \u0026flag;\n 875:\t\t\tbreak;\n 876:\t\n 877:\t\tcase CAN_RAW_RECV_OWN_MSGS:\n 878:\t\t\tif (len \u003e sizeof(int))\n 879:\t\t\t\tlen = sizeof(int);\n"
} |
| 27/3 | 2026/10/22 11:39 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25806 output: 28 thoughts: 160 Thoughts: **Analyzing the `raw_setsockopt_locked` Function in Detail** Okay, let's dissect this. I'm focusing on the `raw_setsockopt_locked` function and, specifically, how it handles the `ro->filter` member. From the snippet, I see that the code is dealing with linking new filters to a socket. It seems like the core logic revolves around a potential single-filter scenario, and that's where things get interesting. If `count` equals 1, we enter that first `if` block. Now, I see `sfilter` is being copied. Aha! `sfilter` must be a `struct can_filter`, as it's defined elsewhere. The `copy_from_sockptr` function presumably handles moving the data from user space. Okay, so that means if there's only one filter being set, `ro->dfilter` gets a *copy* of that `sfilter` data, and the `filter` pointer is set to that new copy. Then `ro->filter` is updated to point to what I'm assuming is the local copy. This is important to note - so, `ro->dfilter` is the local copy of the `sfilter` data. And what is that struct? Let's quickly refresh my memory on the definition of `struct can_filter`. I need to review its members and understand how the data is structured to fully grasp the implication here. I'll need to know this to track the implications for the socket filter. |
| 28/3 | 2026/10/22 11:39 | tool | grepper |
0mArgs:{
"Expression": "struct can_filter \\{",
"PathPrefixes": [
"include/"
]
}Results: {
"Output": "include/uapi/linux/can.h=247=struct sockaddr_can {\n--\ninclude/uapi/linux/can.h-287- */\ninclude/uapi/linux/can.h:288:struct can_filter {\ninclude/uapi/linux/can.h-289-\tcanid_t can_id;\n"
} |
| 29/3 | 2026/10/22 11:39 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26416 output: 38 thoughts: 23 Thoughts: Okay, let's dive into this. Here's my thought process on the matter: **Delving into CAN Header Structures** Right, I need to get into the nitty-gritty of CAN frame formats in the Linux kernel. I'm going to take a look at the relevant header file. Specifically, I'm going to examine the contents of `include/uapi/linux/can.h`, focusing around line 288. I'm assuming that's where the definition of the structures that represent the CAN frames are. I expect to find things like definitions for the CAN ID, the DLC (Data Length Code), the data itself, and any relevant flags. Let's see what the designers of the CAN subsystem have come up with... I want to understand how they've structured the header information. |
| 30/3 | 2026/10/22 11:39 | tool | read-file |
0mArgs:{
"File": "include/uapi/linux/can.h",
"FirstLine": 285,
"LineCount": 20
}Results: {
"Contents": " 285:\t * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can\n 286:\t * filter for error message frames (CAN_ERR_FLAG bit set in mask).\n 287:\t */\n 288:\tstruct can_filter {\n 289:\t\tcanid_t can_id;\n 290:\t\tcanid_t can_mask;\n 291:\t};\n 292:\t\n 293:\t#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */\n 294:\t\n 295:\t#endif /* !_UAPI_CAN_H */\n"
} |
| 31/3 | 2026/10/22 11:39 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26846 output: 37 thoughts: 830 Thoughts: **Analyzing CAN_RAW_FILTER Handling with Zero-Length Option Values** Okay, let's break this down. My immediate concern is how `raw_setsockopt` handles `CAN_RAW_FILTER` when `optlen` is zero. I see that `can_filter` is effectively two 32-bit unsigned integers, totaling 8 bytes. When the count is 1, a `copy_from_sockptr` operation is used, copying 8 bytes. For counts greater than 1, `memdup_sockptr` is invoked, copying `optlen` bytes into dynamically allocated kernel memory. My question is, what if `optlen` is zero? Looking at the code, specifically the snippet from `raw_setsockopt`, I see a clear handling of this case. The check `optlen % sizeof(struct can_filter) != 0` is bypassed since 0 modulo any non-zero value is 0. The subsequent check, `optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter)` is also skipped as 0 is not greater than anything. `count` then becomes 0. Now, because `count` is 0, neither the `count > 1` nor the `count == 1` branches are entered. Consequently, `filter` remains `NULL`. Diving into `raw_setsockopt_locked`, with `count` now equal to 0, and `filter` being `NULL`, a few key things happen. The `raw_enable_filters` function gets called with `filter` as `NULL` and `count` as 0. A quick review of `raw_enable_filters` reveals that it iterates from 0 up to, but not including count. Meaning, with a count of 0, the loop body is not executed. This guarantees a safe, null-operation. The subsequent line sets `ro->filter = NULL`, and `ro->count = 0`, as expected. Importantly, all of this occurs without any memory allocation or potential use-after-free issues. The old filters are properly freed via `kfree(ro->filter)` and removed. So, setting `CAN_RAW_FILTER` to an empty filter set appears safe and well-handled; I need to verify that `can_reset_pkg_stats` is safe with the `can_pkg_stats` structure definition. |
| 32/3 | 2026/10/22 11:40 | tool | grepper |
0mArgs:{
"Expression": "struct can_pkg_stats \\{",
"PathPrefixes": [
"include/",
"net/can/"
]
}Results: {
"Output": "net/can/af_can.h=51=struct receiver {\n--\nnet/can/af_can.h-65-/* can be reset e.g. by can_init_stats() */\nnet/can/af_can.h:66:struct can_pkg_stats {\nnet/can/af_can.h-67-\tunsigned long jiffies_init;\n"
} |
| 33/3 | 2026/10/22 11:40 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28334 output: 36 thoughts: 21 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `af_can.h` - A Quick Check** Okay, time to get my bearings. I need a quick refresher on the CAN socket address family, specifically the structures defined in the Linux kernel. Let's start with `net/can/af_can.h`. I'm going to jump to around line 66, because that's usually where the core definitions of the socket address structures start. I want to see how the kernel represents a CAN address. I'm looking for the foundational data structures and flags. No need to go into the weeds just yet, just get a high level overview of the basics. Let's see what we've got. |
| 34/3 | 2026/10/22 11:40 | tool | read-file |
0mArgs:{
"File": "net/can/af_can.h",
"FirstLine": 60,
"LineCount": 40
}Results: {
"Contents": " 60:\t\tstruct rcu_head rcu;\n 61:\t};\n 62:\t\n 63:\t/* statistic structures */\n 64:\t\n 65:\t/* can be reset e.g. by can_init_stats() */\n 66:\tstruct can_pkg_stats {\n 67:\t\tunsigned long jiffies_init;\n 68:\t\n 69:\t\tatomic_long_t rx_frames;\n 70:\t\tatomic_long_t tx_frames;\n 71:\t\tatomic_long_t matches;\n 72:\t\n 73:\t\tunsigned long total_rx_rate;\n 74:\t\tunsigned long total_tx_rate;\n 75:\t\tunsigned long total_rx_match_ratio;\n 76:\t\n 77:\t\tunsigned long current_rx_rate;\n 78:\t\tunsigned long current_tx_rate;\n 79:\t\tunsigned long current_rx_match_ratio;\n 80:\t\n 81:\t\tunsigned long max_rx_rate;\n 82:\t\tunsigned long max_tx_rate;\n 83:\t\tunsigned long max_rx_match_ratio;\n 84:\t\n 85:\t\tatomic_long_t rx_frames_delta;\n 86:\t\tatomic_long_t tx_frames_delta;\n 87:\t\tatomic_long_t matches_delta;\n 88:\t};\n 89:\t\n 90:\t/* persistent statistics */\n 91:\tstruct can_rcv_lists_stats {\n 92:\t\tunsigned long stats_reset;\n 93:\t\tunsigned long user_reset;\n 94:\t\tunsigned long rcv_entries;\n 95:\t\tunsigned long rcv_entries_max;\n 96:\t};\n 97:\t\n 98:\t/* function prototypes for the CAN networklayer procfs (proc.c) */\n 99:\tvoid can_init_proc(struct net *net);\n"
} |
| 35/3 | 2026/10/22 11:40 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29058 output: 20 thoughts: 1333 Thoughts: **Analyzing CAN Receiver Registration and Statistics** Okay, let's break this down systematically. First, I'm examining the `can_pkg_stats` struct initialization to ensure data integrity. I've confirmed that every field within the struct is explicitly reset: `jiffies_init` is set using `jiffies`, the atomic counters `rx_frames`, `tx_frames`, `matches`, `rx_frames_delta`, `tx_frames_delta`, and `matches_delta` are all initialized to zero using `atomic_long_set`, and the rate fields (`total_rx_rate`, `total_tx_rate`, `total_rx_match_ratio`, `current_rx_rate`, `current_tx_rate`, `current_rx_match_ratio`, `max_rx_rate`, `max_tx_rate`, and `max_rx_match_ratio`) are set to zero. Given `kzalloc_obj` was used, all fields should be zero anyway, but the explicit initialization is good practice for clarity and potential future changes. Now, let's pivot to `can_print_rcvlist` in `net/can/proc.c`, where I'm trying to trace the origin of `r->ino`. It's used to print the inode number of a receiver, but I need to understand its initialization. The critical point is that `r->ino` is initialized in `can_rx_register`. In `can_rx_register`, I see that `r->ino` is assigned the value of the `ino` argument passed to the function. Now, where is `can_rx_register` called? The call sites are `bcm_rx_setup`, `cgw_register_filter`, `isotp_bind`, `j1939_can_rx_register`, `raw_enable_filters` and `raw_enable_errfilter`. The common thread is that some of these calls pass `sock_i_ino(sk)`. This function is interesting. My guess is it probably just returns the inode number from the socket. I'll need to double check how this interacts when the socket is uninitialized, but it appears that for a socket it's using the socket's inode number (`SOCK_INODE(sk->sk_socket)->i_ino`) and for others zero. To be extra thorough, I'm going to search the entire kernel codebase to verify that there aren't any other hidden calls to `can_rx_register`. I need to ensure nothing is missed. |
| 36/3 | 2026/10/22 11:40 | tool | grepper |
0mArgs:{
"Expression": "can_rx_register\\("
}Results: {
"Output": "include/linux/can/core.h=49=extern void can_proto_unregister(const struct can_proto *cp);\ninclude/linux/can/core.h-50-\ninclude/linux/can/core.h:51:int can_rx_register(struct net *net, struct net_device *dev,\ninclude/linux/can/core.h-52-\t\t canid_t can_id, canid_t mask,\n--\nnet/can/af_can.c=366=static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,\n--\nnet/can/af_can.c-443- */\nnet/can/af_can.c:444:int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,\nnet/can/af_can.c-445-\t\t canid_t mask, void (*func)(struct sk_buff *, void *),\n--\nnet/can/af_can.c=495=static void can_rx_delete_receiver(struct rcu_head *rp)\n--\nnet/can/af_can.c-517- * so the subscription is found even when the device has been moved to\nnet/can/af_can.c:518: * another network namespace after can_rx_register(). The caller has to\nnet/can/af_can.c-519- * ensure that @dev stays valid, e.g. by holding a reference to @dev or\nnet/can/af_can.c-520- * RTNL. @net should be the namespace that has been used for\nnet/can/af_can.c:521: * can_rx_register(). It selects the 'any' CAN device lists for\nnet/can/af_can.c-522- * @dev == NULL and is otherwise only used for the receive list\n--\nnet/can/bcm.c-84-\nnet/can/bcm.c:85:/* get best masking value for can_rx_register() for a given single can_id */\nnet/can/bcm.c-86-#define REGMASK(id) ((id \u0026 CAN_EFF_FLAG) ? \\\n--\nnet/can/bcm.c=1302=static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,\n--\nnet/can/bcm.c-1466-\nnet/can/bcm.c:1467:\t\t/* call can_rx_register() */\nnet/can/bcm.c-1468-\t\tdo_rx_register = 1;\n--\nnet/can/bcm.c-1522-\t\t\tif (dev) {\nnet/can/bcm.c:1523:\t\t\t\terr = can_rx_register(sock_net(sk), dev,\nnet/can/bcm.c-1524-\t\t\t\t\t\t op-\u003ecan_id,\n--\nnet/can/bcm.c-1548-\t\t} else {\nnet/can/bcm.c:1549:\t\t\terr = can_rx_register(sock_net(sk), NULL, op-\u003ecan_id,\nnet/can/bcm.c-1550-\t\t\t\t\t REGMASK(op-\u003ecan_id),\n--\nnet/can/gw.c=579=static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)\nnet/can/gw.c-580-{\nnet/can/gw.c:581:\treturn can_rx_register(net, gwj-\u003esrc.dev, gwj-\u003eccgw.filter.can_id,\nnet/can/gw.c-582-\t\t\t gwj-\u003eccgw.filter.can_mask, can_can_gw_rcv,\n--\nnet/can/isotp.c=1545=static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len)\n--\nnet/can/isotp.c-1643-\tif (isotp_register_rxid(so))\nnet/can/isotp.c:1644:\t\tcan_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),\nnet/can/isotp.c-1645-\t\t\t\tisotp_rcv, sk, sock_i_ino(sk), sk);\n--\nnet/can/isotp.c-1650-\t/* register for echo skb's */\nnet/can/isotp.c:1651:\tcan_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),\nnet/can/isotp.c-1652-\t\t\tisotp_rcv_echo, sk, sock_i_ino(sk), sk);\n--\nnet/can/j1939/main.c=175=void j1939_priv_get(struct j1939_priv *priv)\n--\nnet/can/j1939/main.c-179-\nnet/can/j1939/main.c:180:static int j1939_can_rx_register(struct j1939_priv *priv)\nnet/can/j1939/main.c-181-{\n--\nnet/can/j1939/main.c-185-\tj1939_priv_get(priv);\nnet/can/j1939/main.c:186:\tret = can_rx_register(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,\nnet/can/j1939/main.c-187-\t\t\t j1939_can_recv, priv, 0, NULL);\n--\nnet/can/j1939/main.c=254=struct j1939_priv *j1939_netdev_start(struct net_device *ndev)\n--\nnet/can/j1939/main.c-289-\nnet/can/j1939/main.c:290:\tret = j1939_can_rx_register(priv);\nnet/can/j1939/main.c-291-\tif (ret \u003c 0)\n--\nnet/can/raw.c=220=static int raw_enable_filters(struct net *net, struct net_device *dev,\n--\nnet/can/raw.c-228-\t\tcond_resched();\nnet/can/raw.c:229:\t\terr = can_rx_register(net, dev, filter[i].can_id,\nnet/can/raw.c-230-\t\t\t\t filter[i].can_mask,\n--\nnet/can/raw.c=245=static int raw_enable_errfilter(struct net *net, struct net_device *dev,\n--\nnet/can/raw.c-250-\tif (err_mask)\nnet/can/raw.c:251:\t\terr = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,\nnet/can/raw.c-252-\t\t\t\t raw_rcv, sk, sock_i_ino(sk), sk);\n"
} |
| 37/3 | 2026/10/22 11:40 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32812 output: 38 thoughts: 1013 Thoughts: **Analyzing Potential Kernel Information Leaks in CAN Bus Driver** Okay, all the caller updates are confirmed as sound. Time to dive into the changes for `can_print_rcvlist`. Previously, we were directly printing kernel pointers (`r->func`, `r->data`) using `%pK`. Now, we're printing `r->ino` and symbol names using `%ps` (or `(unknown)`). This immediately improves security by avoiding direct kernel address exposure, especially when `kptr_restrict` isn't active. My primary concern here, from a security standpoint, isn't about the pointers themselves being visible (although that's a plus); it's about whether this change introduces any uninitialized memory reads. However, a quick scan indicates no such vulnerability. The `symcheck` buffer is explicitly written to by `snprintf` *before* the check. This writes a null-terminated string, eliminating the possibility of reading uninitialized stack data. Furthermore, `r->ino` is guaranteed to be initialized during allocation. Good. No immediate red flags here. This is a solid security improvement without introducing new risks. Now, let's scrutinize `bcm_proc_show`. The earlier version printed socket pointers (`sk->sk_socket`, `sk`, `bo`) directly. The new code prints `bo->procname`. My immediate thought is, "Where does `bo->procname` come from?" A quick jump to `bcm_connect` reveals the initialization: a formatted string using the socket's inode number (`sock_i_ino(sk)`) using `snprintf`. The concern is whether `bcm_proc_show` could be called before `bcm_connect` has been called, leading to a read of uninitialized memory. But `bcm_proc_show` is only registered with the proc file that is created in `bcm_connect`, and its name is dependent on the allocation. Moreover, the socket itself, `bcm_sock`, is allocated via `sk_alloc` (which zero-initializes). So, this confirms that the `bcm_proc_show` is only called once the inode number has been created. Therefore, no potential for a read of uninitialized data in that code path. Excellent. All looks safe and sound. |
| 38/3 | 2026/10/22 11:40 | tool | grepper |
0mError:no matches Args: {
"Expression": "sk_alloc.*CAN_BCM|bcm_create",
"PathPrefixes": [
"net/can/"
]
}Results: {
"error": "no matches"
} |
| 39/3 | 2026/10/22 11:40 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34368 output: 32 thoughts: 22 Thoughts: Here's my thought process, as if I were considering this myself: **Tracing the Allocation of `bcm_sock` in the CAN Subsystem** Alright, let's get down to brass tacks. I need to pinpoint where the `bcm_sock` structure is being allocated within the CAN subsystem's BCM (Broadcast Manager) implementation. My instincts tell me that the primary location is likely to be `net/can/bcm.c`. That's the first place I'll start digging. It's the most logical source file to house the core functionality for that protocol. From there, I'll be looking for memory allocation functions like `kmalloc`, `kzalloc`, or `slab` allocations, specifically targeting the `bcm_sock` structure or something closely related that then *becomes* a `bcm_sock`. I'll also be watching out for any wrappers around those functions that might be involved in the allocation process. Time to dive into the code and see what I find. |
| 40/3 | 2026/10/22 11:41 | tool | grepper |
0mError:no matches Args: {
"Expression": "sk_alloc",
"PathPrefixes": [
"net/can/bcm.c"
]
}Results: {
"error": "no matches"
} |
| 41/3 | 2026/10/22 11:41 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34651 output: 31 thoughts: 17 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Hunting for the Key Initialization Functions** Alright, let's get down to brass tacks. I need to understand this system's initialization sequence, especially how it handles protocol setup within the Broadcom infrastructure. My initial instinct is to zero in on either `bcm_init` or `bcm_proto`. These are the likely candidates for the core initialization or protocol-specific setup routines within a Broadcom environment. I'm assuming that either one, or both, will give me a good entry point to trace the system's startup and resource allocation. Finding either of those routines should quickly reveal the surrounding code and give me a clear picture. |
| 42/3 | 2026/10/22 11:41 | tool | grepper |
0mArgs:{
"Expression": "struct proto bcm_proto",
"PathPrefixes": [
"net/can/"
]
}Results: {
"Output": "net/can/bcm.c=2092=static const struct proto_ops bcm_ops = {\n--\nnet/can/bcm.c-2109-\nnet/can/bcm.c:2110:static struct proto bcm_proto __read_mostly = {\nnet/can/bcm.c-2111-\t.name = \"CAN_BCM\",\n"
} |
| 43/3 | 2026/10/22 11:41 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34962 output: 37 thoughts: 22 Thoughts: Here's a summary of my mental process: **Diving into the BCM Driver Code** Okay, time to get my hands dirty. I need to understand this specific aspect of the CAN bus implementation, so I'm going to start by reading the `net/can/bcm.c` file. Specifically, I'm focusing my attention around line 2110. I'm hoping that particular section contains the information I'm looking for regarding whatever issue or design consideration I'm currently working through. This is the starting point for my deep dive. Let's see what's there. |
| 44/3 | 2026/10/22 11:41 | tool | read-file |
0mArgs:{
"File": "net/can/bcm.c",
"FirstLine": 2110,
"LineCount": 40
}Results: {
"Contents": "2110:\tstatic struct proto bcm_proto __read_mostly = {\n2111:\t\t.name = \"CAN_BCM\",\n2112:\t\t.owner = THIS_MODULE,\n2113:\t\t.obj_size = sizeof(struct bcm_sock),\n2114:\t\t.init = bcm_init,\n2115:\t};\n2116:\t\n2117:\tstatic const struct can_proto bcm_can_proto = {\n2118:\t\t.type = SOCK_DGRAM,\n2119:\t\t.protocol = CAN_BCM,\n2120:\t\t.ops = \u0026bcm_ops,\n2121:\t\t.prot = \u0026bcm_proto,\n2122:\t};\n2123:\t\n2124:\tstatic int canbcm_pernet_init(struct net *net)\n2125:\t{\n2126:\t#if IS_ENABLED(CONFIG_PROC_FS)\n2127:\t\t/* create /proc/net/can-bcm directory */\n2128:\t\tnet-\u003ecan.bcmproc_dir = proc_net_mkdir(net, \"can-bcm\", net-\u003eproc_net);\n2129:\t#endif /* CONFIG_PROC_FS */\n2130:\t\n2131:\t\treturn 0;\n2132:\t}\n2133:\t\n2134:\tstatic void canbcm_pernet_exit(struct net *net)\n2135:\t{\n2136:\t#if IS_ENABLED(CONFIG_PROC_FS)\n2137:\t\t/* remove /proc/net/can-bcm directory */\n2138:\t\tif (net-\u003ecan.bcmproc_dir)\n2139:\t\t\tremove_proc_entry(\"can-bcm\", net-\u003eproc_net);\n2140:\t#endif /* CONFIG_PROC_FS */\n2141:\t}\n2142:\t\n2143:\tstatic struct pernet_operations canbcm_pernet_ops __read_mostly = {\n2144:\t\t.init = canbcm_pernet_init,\n2145:\t\t.exit = canbcm_pernet_exit,\n2146:\t};\n2147:\t\n2148:\tstatic struct notifier_block canbcm_notifier = {\n2149:\t\t.notifier_call = bcm_notifier\n"
} |
| 45/3 | 2026/10/22 11:41 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35785 output: 36 thoughts: 668 Thoughts: **Analyzing Recent Kernel Network Changes** Okay, so I'm looking through these recent kernel changes related to networking, specifically CAN. First, I see that `sk_alloc()` is zeroing out the entire `struct sock` and the protocol's `obj_size`, which is good practice for initialization and security. Then, there's a change in `bcm_connect` where the `procname` field is being populated. I initially noticed it used `snprintf` to format the socket's inode number (`sock_i_ino(sk)`). Good, that's definitely the safer approach compared to the previous, and now replaced, use of `sprintf` with `%pK`, which presented a potential buffer overflow vulnerability. That's a solid improvement. It's a bit unclear, initially, what the *original* change was that prompted the diff. It seems that the original `sprintf` used the pointer itself, but then was changed again to `sock_i_ino`. The check has a small amount of confusion in the diff, but the end result is a good, safer change. Next, I see a shift in `drivers/net/can/dev/skb.c` from `kfree_skb(skb)` to `dev_kfree_skb_any(skb)`. This is interesting; `dev_kfree_skb_any` can often be more efficient, especially in a driver context, as it might bypass some locking or context checks that `kfree_skb` performs. A good performance optimization there. Also, there's a new check in the `ETH_P_CANXL` handling. It now validates if a device has the `CAN_CAP_XL` capability before processing CANXL skbs. That's essential to prevent errors if the device doesn't support the protocol. This is a proper validation to guard against unsupported frame types. Finally, I'm digging into `net/can/isotp.c`. The `isotp_rcv` function is getting a subtle but crucial modification. There's a new check for frame content based on `CAN_MTU`. If the MTU is `CAN_MTU`, it checks the skb is of CAN type, otherwise it checks for CANFD type. This ensures that the function correctly processes CAN and CAN FD frames according to the link layer MTU. This looks like a necessary check to prevent unexpected behavior. I really should check what it *used* to do to get the full picture, since that's a key part of the diff. Okay, I see that the original was missing these checks, which means that the original code may have been more vulnerable to corruption or misuse. |
| 46/3 | 2026/10/22 11:41 | tool | read-file |
0mArgs:{
"File": "net/can/isotp.c",
"FirstLine": 750,
"LineCount": 40
}Results: {
"Contents": " 750:\t\tint ae = (so-\u003eopt.flags \u0026 CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;\n 751:\t\tu8 n_pci_type, sf_dl;\n 752:\t\n 753:\t\t/* Strictly receive only frames with the configured MTU size\n 754:\t\t * =\u003e clear separation of CAN2.0 / CAN FD transport channels\n 755:\t\t */\n 756:\t\tif (skb-\u003elen != so-\u003ell.mtu)\n 757:\t\t\treturn;\n 758:\t\n 759:\t\t/* check for correct CAN CC/FD frame content */\n 760:\t\tif (so-\u003ell.mtu == CAN_MTU) {\n 761:\t\t\tif (!can_is_can_skb(skb))\n 762:\t\t\t\treturn;\n 763:\t\t} else if (!can_is_canfd_skb(skb)) {\n 764:\t\t\treturn;\n 765:\t\t}\n 766:\t\n 767:\t\tcf = (struct canfd_frame *)skb-\u003edata;\n 768:\t\n 769:\t\t/* if enabled: check reception of my configured extended address */\n 770:\t\tif (ae \u0026\u0026 cf-\u003edata[0] != so-\u003eopt.rx_ext_address)\n 771:\t\t\treturn;\n 772:\t\n 773:\t\tn_pci_type = cf-\u003edata[ae] \u0026 0xF0;\n 774:\t\n 775:\t\t/* Make sure the state changes and data structures stay consistent at\n 776:\t\t * CAN frame reception time. This locking is not needed in real world\n 777:\t\t * use cases but the inconsistency can be triggered with syzkaller.\n 778:\t\t */\n 779:\t\tspin_lock(\u0026so-\u003erx_lock);\n 780:\t\n 781:\t\tif (so-\u003eopt.flags \u0026 CAN_ISOTP_HALF_DUPLEX) {\n 782:\t\t\t/* check rx/tx path half duplex expectations */\n 783:\t\t\tif ((READ_ONCE(so-\u003etx.state) != ISOTP_IDLE \u0026\u0026\n 784:\t\t\t n_pci_type != N_PCI_FC) ||\n 785:\t\t\t (READ_ONCE(so-\u003erx.state) != ISOTP_IDLE \u0026\u0026\n 786:\t\t\t n_pci_type == N_PCI_FC))\n 787:\t\t\t\tgoto out_unlock;\n 788:\t\t}\n 789:\t\n"
} |
| 47/3 | 2026/10/22 11:41 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37727 output: 21 thoughts: 2064 Thoughts: **Deep Dive into CAN Subsystem Concurrency and Initialization** Okay, let's break this down systematically. First, the introduction of `can_is_can_skb(skb)` and `can_is_canfd_skb(skb)` is a welcome addition; it's a critical strengthening of CAN/CANFD structure validation. Good. Now, let's dissect the concurrency and lifecycle changes in `isotp_bind`, `isotp_release`, and `isotp_notify`. We're looking at lock acquisitions and releases, device lookups, and the potential for a race condition with the `IFF_UP` flag. Using `rcu_read_lock` and `rcu_read_unlock` with the checks and then RTNL lock removal is expected. The use of `READ_ONCE`, `WRITE_ONCE`, and `smp_mb` is also standard practice. The code touches on concurrency, locking, and lifecycle; KASAN/LOCKDEP would be the first things I would reach for to check for UAF or race conditions here. Moving on to `net/can/raw.c`: The introduction of `bind_dev` to `struct raw_sock` immediately raises flags. I’m looking for the scope of the change and possible race conditions. The checks within the `raw_bind` and `raw_notify` functions for `smp_store_release` and `smp_load_acquire` are necessary to prevent issues. The refactoring of `raw_setsockopt` warrants a closer examination. Moving the copy from user space outside of the RTNL lock is good. It prevents the stalling of the raw_notify() function. Let's re-verify the code. I am carefully reviewing the handling of `CAN_RAW_FILTER`. The code is copying the filters before locking the sock. The count of the filters is then checked. If count is 1, a `copy_from_sockptr` is done. If that fails, it returns `-EFAULT`, which makes sense. The code then checks if the socket name is the filter; if so, if the count is one, then the value is applied to the dfilter. If not, then a memdup_sockptr is done. It copies filters before locking, to prevent stall when waiting for lock_sock() under RTNL. The use of `sfilter` raised a flag, and the examination ensures that the stack-allocated `sfilter` is always fully initialized before use. The conditional logic is sound; it's either populated by `copy_from_sockptr` or completely unused. I see no uninitialized reads of `sfilter`. Also, reviewing the `ro->dfilter`, it is fully initialized in `raw_init`. Excellent. However, the real question is in `can_rx_register` and `can_rx_unregister`. I'm going in for a thorough check of `struct receiver`. Every single member of `struct receiver` needs to be accounted for. The allocation of the `receiver` structure using `kmem_cache_alloc` raises a flag. We're looking at `dev_rcv_lists` and making sure that all of the fields of this `struct` are initialized when a receiver is added. Looking at the code in `net/can/af_can.h`, I see that this is indeed the case. The use of `hlist_add_head_rcu` means we *must* have initialization. The members are being correctly initialized before insertion into the list: `can_id`, `mask`, `matches`, `func`, `data`, `ino`, and `sk` are all set. `struct rcu_head` is for RCU-related operations. All is well here. Let's drill down even deeper into `struct can_dev_rcv_lists`. The structure itself is simple, consisting of a spinlock, some hlist heads, and an integer. Now, the question is: Where is *this* structure initialized? The initialization of the spinlock is the key. The code references `can_set_ml_priv`, and this is where it's done: a spinlock is initialized. Excellent, `kzalloc` is called to initialize the `can_dev_rcv_lists` spinlock. That's another potential race condition eliminated. Finally, the `struct can_ml_priv` is allocated. This is confirmed. Overall, the code appears robust regarding initialization and concurrency, which are good. However, all these changes warrant thorough KASAN/LOCKDEP testing as a part of a wider security review to catch any lingering issues. We need to be absolutely certain. |
| 48/3 | 2026/10/22 11:41 | tool | grepper |
0mArgs:{
"Expression": "can_set_ml_priv"
}Results: {
"Output": "drivers/net/can/dev/dev.c=298=struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,\n--\ndrivers/net/can/dev/dev.c-333-\tcan_ml = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);\ndrivers/net/can/dev/dev.c:334:\tcan_set_ml_priv(dev, can_ml);\ndrivers/net/can/dev/dev.c-335-\tcan_set_cap(dev, CAN_CAP_CC);\n--\ndrivers/net/can/vcan.c=170=static void vcan_setup(struct net_device *dev)\n--\ndrivers/net/can/vcan.c-177-\tdev-\u003eflags\t\t= IFF_NOARP;\ndrivers/net/can/vcan.c:178:\tcan_set_ml_priv(dev, netdev_priv(dev));\ndrivers/net/can/vcan.c-179-\tvcan_set_cap_info(dev);\n--\ndrivers/net/can/vxcan.c=185=static void vxcan_setup(struct net_device *dev)\n--\ndrivers/net/can/vxcan.c-199-\tcan_ml = netdev_priv(dev) + ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN);\ndrivers/net/can/vxcan.c:200:\tcan_set_ml_priv(dev, can_ml);\ndrivers/net/can/vxcan.c-201-\tvxcan_set_cap_info(dev);\n--\ninclude/linux/can/can-ml.h=78=static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)\n--\ninclude/linux/can/can-ml.h-82-\ninclude/linux/can/can-ml.h:83:static inline void can_set_ml_priv(struct net_device *dev,\ninclude/linux/can/can-ml.h-84-\t\t\t\t struct can_ml_priv *ml_priv)\n"
} |
| 49/3 | 2026/10/22 11:41 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41364 output: 405 thoughts: 1797 Thoughts: **KMSAN Assessment: No Need for KernelMemorySanitizer** Okay, so I've been through this patch, line by line, and I need to determine if `NeedsKMSAN` should be set to true. The goal is to identify if this code introduces or modifies something that KMSAN *specifically* detects: uninitialized memory reads or information leaks. KASAN and LOCKDEP are already enabled and will catch other memory safety or locking issues. First, I examined the allocation of `netdev_priv` in various CAN device types. The allocation itself uses `alloc_netdev_mqs`, which initializes private data to zero, and the spinlock initialization confirms standard and safe initialization practices. Next, there's a quick fix that calls `dev_kfree_skb_any` to fix an SKB leak and replace `kfree_skb` with the safe `dev_kfree_skb_any` function where it can be called from any context. I then ran through the critical questions. No kernel structures are being sent to userspace with uninitialized fields. The `/proc/net/can/rcvlist_all` output has changed, but the values printed are now fully initialized or derived from initialized variables. The `snprintf` function is safe within the context of use. Moving on, I inspected the code for conditional logic that depends on potentially uninitialized variables. In `raw_setsockopt`, I checked how `sfilter`, `filter` and `count` were initialized and used. `count` and `filter` are initialized and handled safely. In `isotp_notify` and `raw_notify`, I saw that relevant device pointers were correctly initialized. Regarding complex data structures, I confirmed that `struct receiver`, `can_pkg_stats` and `can_dev_rcv_lists` have explicitly initialized fields or are properly reset. Finally, I checked for bounds checks or logic that might expose uninitialized bytes. The patch adds checks like `!can_cap_enabled(dev, CAN_CAP_XL)` and `can_is_can_skb`, and `can_is_canfd_skb`, which are *restricting* access rather than expanding it. The skb memory freeing is being made more robust, which also removes risk. Overall, the patch primarily focuses on improving safety related to: replacing `kfree_skb`, removing RTNL locking, replacing module identifiers in procfs, and adding checks to CAN frames. These changes *potentially* introduce concurrency issues, memory corruption, or lifecycle bugs, but those are already addressed by KASAN and LOCKDEP. There are no uninitialized memory risks or potential leaks introduced by the patch, so I can confidently set `NeedsKMSAN=false`. Let's call `set-results`. |
| 50/3 | 2026/10/22 11:42 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series refactors CAN networking subsystems (af_can, raw, isotp, bcm, gw, j1939):\n1. Locking \u0026 Concurrency: Replaces RTNL locking with socket locks, RCU device lookups, and per-device receive list spinlocks (dev_rcv_lists-\u003elock). Fixes concurrency races between socket operations and netdev notifier events. Potential bugs here are deadlocks, races, or use-after-free, which are covered by LOCKDEP and KASAN.\n2. Memory Freeing: Replaces kfree_skb/consume_skb with dev_kfree_skb_any/dev_consume_skb_any to ensure safe invocation from atomic or IRQ contexts.\n3. Procfs \u0026 Identifiers: Replaces kernel pointer/ident prints with socket inode numbers (sock_i_ino) and symbol formatting (%ps) in /proc/net/can/rcvlist_*. All fields printed or formatted are explicitly initialized, and all new struct fields (e.g. rcv-\u003eino, ro-\u003ebind_dev, dev_rcv_lists-\u003elock) are fully initialized upon allocation.\n4. Statistics \u0026 Checks: Replaces memset with an explicit can_reset_pkg_stats() that resets all fields of can_pkg_stats, and adds stricter CAN frame validation (can_is_can_skb / CAN_CAP_XL).\n5. setsockopt: CAN_RAW_FILTER copying is moved outside lock_sock; sfilter and dynamically allocated filters are strictly initialized before being linked or read.\n\nNone of the changes introduce risks of uninitialized memory reads, uninitialized structure padding leaks to user space, or branching on uninitialized data. A dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series refactors CAN networking subsystems (af_can, raw, isotp, bcm, gw, j1939):\n1. Locking \u0026 Concurrency: Replaces RTNL locking with socket locks, RCU device lookups, and per-device receive list spinlocks (dev_rcv_lists-\u003elock). Fixes concurrency races between socket operations and netdev notifier events. Potential bugs here are deadlocks, races, or use-after-free, which are covered by LOCKDEP and KASAN.\n2. Memory Freeing: Replaces kfree_skb/consume_skb with dev_kfree_skb_any/dev_consume_skb_any to ensure safe invocation from atomic or IRQ contexts.\n3. Procfs \u0026 Identifiers: Replaces kernel pointer/ident prints with socket inode numbers (sock_i_ino) and symbol formatting (%ps) in /proc/net/can/rcvlist_*. All fields printed or formatted are explicitly initialized, and all new struct fields (e.g. rcv-\u003eino, ro-\u003ebind_dev, dev_rcv_lists-\u003elock) are fully initialized upon allocation.\n4. Statistics \u0026 Checks: Replaces memset with an explicit can_reset_pkg_stats() that resets all fields of can_pkg_stats, and adds stricter CAN frame validation (can_is_can_skb / CAN_CAP_XL).\n5. setsockopt: CAN_RAW_FILTER copying is moved outside lock_sock; sfilter and dynamically allocated filters are strictly initialized before being linked or read.\n\nNone of the changes introduce risks of uninitialized memory reads, uninitialized structure padding leaks to user space, or branching on uninitialized data. A dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|