--- /dev/null
+From de58298c50269931fdf484d18b536074b2cb64c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:35 +0200
+Subject: can: bcm: add locking when updating filter and timer values
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 749179c2e25b95d22499ed29096b3e02d6dfd2b4 upstream.
+
+KCSAN detected a simultaneous access to timer values that can be
+overwritten in bcm_rx_setup() when updating timer and filter content
+while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler()
+run concurrently on incoming CAN traffic.
+
+Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter
+(nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new
+per-op bcm_rx_update_lock, taken with the matching scope in the RX
+handlers. memcpy_from_msg() is staged into a temporary buffer before the
+lock is taken, since it can sleep and must not run under a spinlock.
+
+hrtimer_cancel() is always called without bcm_rx_update_lock held, since
+bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a
+running callback would otherwise deadlock against the canceller.
+
+Also close a related race: bcm_rx_setup() cleared the RTR flag in the
+stored reply frame's can_id as a separate, unprotected step after the
+frame content was already installed, so a concurrent bcm_rx_handler()
+could transmit a stale reply with CAN_RTR_FLAG still set. Fold that
+normalization into the initial frame preparation instead (on the staged
+buffer for updates, directly on op->frames pre-registration for new
+ops), so the installed frame is always atomically self-consistent.
+
+bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected
+snapshot of op->flags before deciding whether to call bcm_can_tx(),
+but does not hold the lock across that call.
+
+Also take a lock-protected snapshot of the currframe in bcm_can_tx()
+to avoid partly overwrites by content updates in bcm_tx_setup().
+Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset
+op->currframe between the two locked sections in bcm_can_tx().
+
+Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler().
+kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it
+cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock.
+
+Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates")
+Reported-by: syzbot+75e5e4ae00c3b4bb544e@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/linux-can/6975d5cf.a00a0220.33ccc7.0022.GAE@google.com/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-3-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 178 +++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 134 insertions(+), 44 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index ee2b14e56b17b6..131c7783b92d33 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -126,6 +126,7 @@ struct bcm_op {
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+ spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
++ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
+ };
+
+ struct bcm_sock {
+@@ -280,21 +281,27 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+ */
+-static void bcm_can_tx(struct bcm_op *op)
++static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)
+ {
+ struct sk_buff *skb;
+ struct net_device *dev;
+- struct canfd_frame *cf;
++ struct canfd_frame cframe;
++ bool cyclic = !cf;
++ unsigned int idx = 0;
+ int err;
+
+ /* no target device? => exit */
+ if (!op->ifindex)
+ return;
+
+- /* read currframe under lock protection */
+- spin_lock_bh(&op->bcm_tx_lock);
+- cf = op->frames + op->cfsiz * op->currframe;
+- spin_unlock_bh(&op->bcm_tx_lock);
++ if (cyclic) {
++ /* read currframe under lock protection */
++ spin_lock_bh(&op->bcm_tx_lock);
++ idx = op->currframe;
++ memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz);
++ cf = &cframe;
++ spin_unlock_bh(&op->bcm_tx_lock);
++ }
+
+ dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
+ if (!dev) {
+@@ -323,14 +330,20 @@ static void bcm_can_tx(struct bcm_op *op)
+ if (!err)
+ op->frames_abs++;
+
+- op->currframe++;
++ /* only advance the cyclic sequence if nothing reset currframe while
++ * we were sending - a concurrent TX_RESET_MULTI_IDX means this
++ * frame's bookkeeping belongs to a sequence that no longer exists
++ */
++ if (!cyclic || op->currframe == idx) {
++ op->currframe++;
+
+- /* reached last frame? */
+- if (op->currframe >= op->nframes)
+- op->currframe = 0;
++ /* reached last frame? */
++ if (op->currframe >= op->nframes)
++ op->currframe = 0;
+
+- if (op->count > 0)
+- op->count--;
++ if (op->count > 0)
++ op->count--;
++ }
+
+ spin_unlock_bh(&op->bcm_tx_lock);
+ out:
+@@ -429,7 +442,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ struct bcm_msg_head msg_head;
+
+ if (op->kt_ival1 && (op->count > 0)) {
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+ if (!op->count && (op->flags & TX_COUNTEVT)) {
+
+ /* create notification to user */
+@@ -446,7 +459,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ }
+
+ } else if (op->kt_ival2) {
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+ }
+
+ return bcm_tx_set_expiry(op, &op->timer) ?
+@@ -585,6 +598,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+ struct bcm_msg_head msg_head;
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+ /* clear received CAN frames to indicate 'nothing received' */
+@@ -601,6 +616,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+
+ return HRTIMER_NORESTART;
+@@ -649,15 +666,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op)
+ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
+ {
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
++ enum hrtimer_restart ret;
+
+- if (bcm_rx_thr_flush(op)) {
+- hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+- return HRTIMER_RESTART;
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
++ /* kt_ival2 may have been concurrently cleared by bcm_rx_setup()
++ * before it cancels this timer - never forward with a zero
++ * interval in that case.
++ */
++ if (bcm_rx_thr_flush(op) && op->kt_ival2) {
++ hrtimer_forward_now(hrtimer, op->kt_ival2);
++ ret = HRTIMER_RESTART;
+ } else {
+ /* rearm throttle handling */
+ op->kt_lastmsg = 0;
+- return HRTIMER_NORESTART;
++ ret = HRTIMER_NORESTART;
+ }
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ return ret;
+ }
+
+ /*
+@@ -667,7 +695,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ {
+ struct bcm_op *op = (struct bcm_op *)data;
+ const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
++ struct canfd_frame rtrframe;
+ unsigned int i;
++ bool rtr_frame;
+
+ if (op->can_id != rxframe->can_id)
+ return;
+@@ -686,12 +716,23 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ /* update statistics */
+ op->frames_abs++;
+
+- if (op->flags & RX_RTR_FRAME) {
++ /* snapshot the flag under lock: op->flags/op->frames may be updated
++ * concurrently by bcm_rx_setup().
++ */
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ rtr_frame = op->flags & RX_RTR_FRAME;
++ if (rtr_frame)
++ memcpy(&rtrframe, op->frames, op->cfsiz);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ if (rtr_frame) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
+- bcm_can_tx(op);
++ bcm_can_tx(op, &rtrframe);
+ return;
+ }
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ bcm_rx_update_and_send(op, op->last_frames, rxframe);
+@@ -725,6 +766,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+
+ rx_starttimer:
+ bcm_rx_starttimer(op);
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+
+ /*
+@@ -1068,7 +1111,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ list_add_rcu(&op->list, &bo->tx_ops);
+
+ if (op->flags & TX_ANNOUNCE)
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+
+ if (op->flags & STARTTIMER)
+ bcm_tx_start_timer(op);
+@@ -1082,6 +1125,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
++static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
++ struct bcm_op *op, void *new_frames)
++{
++ /* funny feature in RX(!)_SETUP only for RTR-mode:
++ * copy can_id into frame BUT without RTR-flag to
++ * prevent a full-load-loopback-test ... ;-]
++ * normalize this on the staged buffer, before it is
++ * ever installed into op->frames.
++ */
++ if (msg_head->flags & RX_RTR_FRAME) {
++ struct canfd_frame *frame0 = new_frames;
++
++ if ((msg_head->flags & TX_CP_CAN_ID) ||
++ frame0->can_id == op->can_id)
++ frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
++ }
++}
++
+ /*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+@@ -1116,6 +1177,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check the given can_id */
+ op = bcm_find_op(&bo->rx_ops, msg_head, ifindex);
+ if (op) {
++ void *new_frames = NULL;
++
+ /* update existing BCM operation */
+
+ /*
+@@ -1127,19 +1190,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return -E2BIG;
+
+ if (msg_head->nframes) {
+- /* update CAN frames content */
+- err = memcpy_from_msg(op->frames, msg,
++ /* get new CAN frames content before locking */
++ new_frames = kmalloc(msg_head->nframes * op->cfsiz,
++ GFP_KERNEL);
++ if (!new_frames)
++ return -ENOMEM;
++
++ err = memcpy_from_msg(new_frames, msg,
+ msg_head->nframes * op->cfsiz);
+- if (err < 0)
++ if (err < 0) {
++ kfree(new_frames);
+ return err;
++ }
+
+- /* clear last_frames to indicate 'nothing received' */
+- memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
++ bcm_rx_setup_rtr_check(msg_head, op, new_frames);
+ }
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
+ op->nframes = msg_head->nframes;
+ op->flags = msg_head->flags;
+
++ if (msg_head->nframes) {
++ /* update CAN frames content */
++ memcpy(op->frames, new_frames,
++ msg_head->nframes * op->cfsiz);
++
++ /* clear last_frames to indicate 'nothing received' */
++ memset(op->last_frames, 0,
++ msg_head->nframes * op->cfsiz);
++ }
++
++ if (msg_head->flags & SETTIMER) {
++ op->ival1 = msg_head->ival1;
++ op->ival2 = msg_head->ival2;
++ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
++ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ op->kt_lastmsg = 0;
++ }
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ /* free temporary frames / kfree(NULL) is safe */
++ kfree(new_frames);
++
+ /* Only an update -> do not call can_rx_register() */
+ do_rx_register = 0;
+
+@@ -1150,6 +1242,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return -ENOMEM;
+
+ spin_lock_init(&op->bcm_tx_lock);
++ spin_lock_init(&op->bcm_rx_update_lock);
+ op->can_id = msg_head->can_id;
+ op->nframes = msg_head->nframes;
+ op->cfsiz = CFSIZ(msg_head->flags);
+@@ -1191,6 +1284,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ kfree(op);
+ return err;
+ }
++
++ bcm_rx_setup_rtr_check(msg_head, op, op->frames);
+ }
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+@@ -1218,29 +1313,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check flags */
+
+ if (op->flags & RX_RTR_FRAME) {
+- struct canfd_frame *frame0 = op->frames;
+-
+ /* no timers in RTR-mode */
+ hrtimer_cancel(&op->thrtimer);
+ hrtimer_cancel(&op->timer);
+-
+- /*
+- * funny feature in RX(!)_SETUP only for RTR-mode:
+- * copy can_id into frame BUT without RTR-flag to
+- * prevent a full-load-loopback-test ... ;-]
+- */
+- if ((op->flags & TX_CP_CAN_ID) ||
+- (frame0->can_id == op->can_id))
+- frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+-
+ } else {
+ if (op->flags & SETTIMER) {
+
+- /* set timer value */
+- op->ival1 = msg_head->ival1;
+- op->ival2 = msg_head->ival2;
+- op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+- op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ /* set timers (locked) for newly created op */
++ if (do_rx_register) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ op->ival1 = msg_head->ival1;
++ op->ival2 = msg_head->ival2;
++ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
++ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ op->kt_lastmsg = 0;
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
+
+ /* disable an active timer due to zero value? */
+ if (!op->kt_ival1)
+@@ -1250,9 +1338,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ * In any case cancel the throttle timer, flush
+ * potentially blocked msgs and reset throttle handling
+ */
+- op->kt_lastmsg = 0;
+ hrtimer_cancel(&op->thrtimer);
++
++ spin_lock_bh(&op->bcm_rx_update_lock);
+ bcm_rx_thr_flush(op);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+
+ if ((op->flags & STARTTIMER) && op->kt_ival1)
+--
+2.53.0
+
--- /dev/null
+From 2deb1956ecd5c0864de65e9a417aa27f2d66f1b6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:39 +0200
+Subject: can: bcm: add missing device refcount for CAN filter removal
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit d59948293ea34b6337ce2b5febab8510de70048c upstream.
+
+sashiko-bot remarked a problem with a concurrent device unregistration
+in isotp.c which also is present in the bcm.c code. A former fix for raw.c
+commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix")
+introduced a netdevice_tracker which solves the issue for bcm.c too.
+
+bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on
+dev_get_by_index(ifindex) to re-find the device for an rx_op before
+unregistering its filter. If a concurrent NETDEV_UNREGISTER has already
+unlisted the device from the ifindex table, that lookup fails and
+can_rx_unregister() is silently skipped, leaving a stale CAN filter
+pointing at the soon-to-be-freed bcm_op/socket.
+
+Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev
+from the moment the rx filter is registered in bcm_rx_setup() until it
+is unregistered in bcm_rx_unreg(), and use that reference directly in
+bcm_release() and bcm_delete_rx_op() instead of re-looking the device
+up by ifindex.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://sashiko.dev/#/patchset/20260707094716.63578-1-socketcan@hartkopp.net
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-8-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 44 ++++++++++++++++++++++++--------------------
+ 1 file changed, 24 insertions(+), 20 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index d74c5d1e29358f..59fc7cee12f41f 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -884,6 +884,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+
+ /* mark as removed subscription */
+ op->rx_reg_dev = NULL;
++ dev_put(dev);
+ } else
+ printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+ "mismatch %p %p\n", op->rx_reg_dev, dev);
+@@ -914,17 +915,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
+ * 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.
+ */
+- if (op->rx_reg_dev) {
+- struct net_device *dev;
+-
+- dev = dev_get_by_index(sock_net(op->sk),
+- op->ifindex);
+- if (dev) {
+- bcm_rx_unreg(dev, op);
+- dev_put(dev);
+- }
+- }
++ if (op->rx_reg_dev)
++ bcm_rx_unreg(op->rx_reg_dev, op);
+ } else
+ can_rx_unregister(sock_net(op->sk), NULL,
+ op->can_id,
+@@ -1447,7 +1445,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ bcm_rx_handler, op,
+ "bcm", sk);
+
+- op->rx_reg_dev = dev;
++ /* keep a reference so that a later
++ * unregister can safely reach the device even
++ * if a concurrent NETDEV_UNREGISTER has
++ * already unlisted it by ifindex
++ */
++ if (!err) {
++ op->rx_reg_dev = dev;
++ dev_hold(dev);
++ }
+ dev_put(dev);
+ } else {
+ /* the requested device is gone - do not
+@@ -1820,16 +1826,14 @@ static int bcm_release(struct socket *sock)
+ * 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.
+ */
+- if (op->rx_reg_dev) {
+- struct net_device *dev;
+-
+- dev = dev_get_by_index(net, op->ifindex);
+- if (dev) {
+- bcm_rx_unreg(dev, op);
+- dev_put(dev);
+- }
+- }
++ if (op->rx_reg_dev)
++ bcm_rx_unreg(op->rx_reg_dev, op);
+ } else
+ can_rx_unregister(net, NULL, op->can_id,
+ REGMASK(op->can_id),
+--
+2.53.0
+
--- /dev/null
+From 81357d5205b8f9e6b5d1798da1b2efb917fc8411 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 18:39:02 +0200
+Subject: can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF
+
+From: Lee Jones <lee@kernel.org>
+
+commit 68973f9db76144825e4f35dfdc80fb8279eb2d57 upstream.
+
+Commit f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly
+synchronize_rcu()") replaced synchronize_rcu() in bcm_delete_rx_op()
+with call_rcu() and introduced the RX_NO_AUTOTIMER flag.
+
+However, this flag check was omitted for thrtimer in the packet rx
+fast-path. During BCM RX operation teardown, a concurrent RCU reader
+(bcm_rx_handler) can race and re-arm thrtimer via
+bcm_rx_update_and_send() after call_rcu() has been scheduled. Once
+the RCU grace period elapses, bcm_op is freed. The subsequently
+firing thrtimer then dereferences the deallocated op, causing a UAF.
+
+Adding flag checks to the rx fast-path (bcm_rx_update_and_send) does not
+fully close the TOCTOU race and introduces latency for every CAN frame.
+Conversely, calling hrtimer_cancel() directly inside the RCU callback
+(softirq context) is fatal as hrtimer_cancel() can sleep, triggering
+a "scheduling while atomic" panic.
+
+Resolve this by deferring the timer cancellation and memory free to a
+dedicated unbound workqueue (bcm_wq). The RCU callback now queues a
+work item to bcm_wq, which safely cancels both timers and deallocates
+memory in sleepable process context. A dedicated workqueue is used to
+prevent system-wide WQ saturation and is cleanly flushed/destroyed
+on module unload to avoid rmmod page faults.
+
+Since the deferred work can now outlive the calling context by an
+unbounded amount, also take a reference on op->sk when it is assigned
+and drop it only once the deferred work has cancelled both timers, so a
+socket can no longer be freed out from under a still-armed timer whose
+callback (bcm_send_to_user()) dereferences op->sk.
+
+Fixes: f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()")
+Tested-by: Feng Xue <feng.xue@outlook.com>
+Tested-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-1-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 34 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 06ee3bfe87c99d..ee2b14e56b17b6 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -58,6 +58,7 @@
+ #include <linux/can/skb.h>
+ #include <linux/can/bcm.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
+ #include <linux/spinlock.h>
+ #include <net/sock.h>
+ #include <net/net_namespace.h>
+@@ -89,6 +90,8 @@ MODULE_ALIAS("can-proto-2");
+
+ #define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
+
++static struct workqueue_struct *bcm_wq;
++
+ /*
+ * easy access to the first 64 bit of can(fd)_frame payload. cp->data is
+ * 64 bit aligned so the offset has to be multiples of 8 which is ensured
+@@ -102,6 +105,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
+ struct bcm_op {
+ struct list_head list;
+ struct rcu_head rcu;
++ struct work_struct work;
+ int ifindex;
+ canid_t can_id;
+ u32 flags;
+@@ -740,9 +744,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
+ return NULL;
+ }
+
+-static void bcm_free_op_rcu(struct rcu_head *rcu_head)
++static void bcm_free_op_work(struct work_struct *work)
+ {
+- struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
++ struct bcm_op *op = container_of(work, struct bcm_op, work);
++
++ hrtimer_cancel(&op->timer);
++ hrtimer_cancel(&op->thrtimer);
+
+ if ((op->frames) && (op->frames != &op->sframe))
+ kfree(op->frames);
+@@ -750,9 +757,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+ if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+ kfree(op->last_frames);
+
++ /* the last possible access to op->timer/op->thrtimer has now
++ * happened above via hrtimer_cancel() - op->sk is no longer
++ * needed by any pending timer callback, so drop our reference
++ */
++ sock_put(op->sk);
++
+ kfree(op);
+ }
+
++static void bcm_free_op_rcu(struct rcu_head *rcu_head)
++{
++ struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
++
++ INIT_WORK(&op->work, bcm_free_op_work);
++ queue_work(bcm_wq, &op->work);
++}
++
+ static void bcm_remove_op(struct bcm_op *op)
+ {
+ hrtimer_cancel(&op->timer);
+@@ -1008,6 +1029,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
++ sock_hold(sk);
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+@@ -1173,6 +1195,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
++ sock_hold(sk);
+ op->ifindex = ifindex;
+
+ /* ifindex for timeout events w/o previous frame reception */
+@@ -1830,11 +1853,16 @@ static int __init bcm_module_init(void)
+ {
+ int err;
+
++ bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0);
++ if (!bcm_wq)
++ return -ENOMEM;
++
+ pr_info("can: broadcast manager protocol\n");
+
+ err = can_proto_register(&bcm_can_proto);
+ if (err < 0) {
+ printk(KERN_ERR "can: registration of bcm protocol failed\n");
++ destroy_workqueue(bcm_wq);
+ return err;
+ }
+
+@@ -1848,6 +1876,8 @@ static void __exit bcm_module_exit(void)
+ can_proto_unregister(&bcm_can_proto);
+ unregister_netdevice_notifier(&canbcm_notifier);
+ unregister_pernet_subsys(&canbcm_pernet_ops);
++ rcu_barrier();
++ destroy_workqueue(bcm_wq);
+ }
+
+ module_init(bcm_module_init);
+--
+2.53.0
+
--- /dev/null
+From 45e930037ec1b9eac10d49cf525c931519a94714 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:37 +0200
+Subject: can: bcm: extend bcm_tx_lock usage for data and timer updates
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc upstream.
+
+Stage new CAN frame content for an existing tx op into a kmalloc()'d
+buffer and validate it there, mirroring the approach already used in
+bcm_rx_setup(). Only copy the validated data into op->frames while
+holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler()
+can no longer observe a partially updated or unvalidated frame.
+
+Add a missing error path for memcpy_from_msg() when copying CAN frame
+data from userspace.
+
+Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup()
+under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same
+lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the
+torn 64-bit ktime_t read on 32-bit platforms.
+
+Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 104 ++++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 75 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 5d5904076d211d..93552b65c00ee4 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -125,7 +125,7 @@ struct bcm_op {
+ struct canfd_frame last_sframe;
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+- spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
++ spinlock_t bcm_tx_lock; /* protect tx data and timer updates */
+ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
+ };
+
+@@ -440,12 +440,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
+ {
+ ktime_t ival;
+
++ spin_lock_bh(&op->bcm_tx_lock);
++
+ if (op->kt_ival1 && op->count)
+ ival = op->kt_ival1;
+- else if (op->kt_ival2)
++ else if (op->kt_ival2) {
+ ival = op->kt_ival2;
+- else
++ } else {
++ spin_unlock_bh(&op->bcm_tx_lock);
+ return false;
++ }
++
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+ hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
+ return true;
+@@ -462,25 +468,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ {
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+ struct bcm_msg_head msg_head;
++ bool tx_ival1, tx_ival2;
++
++ /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn
++ * ktime_t reads racing with concurrent bcm_tx_setup() updates
++ */
++ spin_lock_bh(&op->bcm_tx_lock);
++ tx_ival1 = op->kt_ival1 && (op->count > 0);
++ tx_ival2 = !!op->kt_ival2;
++ spin_unlock_bh(&op->bcm_tx_lock);
++
++ if (tx_ival1) {
++ u32 flags, count;
++ struct bcm_timeval ival1, ival2;
+
+- if (op->kt_ival1 && (op->count > 0)) {
+ bcm_can_tx(op, NULL);
+- if (!op->count && (op->flags & TX_COUNTEVT)) {
+
++ /* snapshot variables under lock to avoid torn reads racing
++ * with concurrent bcm_tx_setup() updates
++ */
++ spin_lock_bh(&op->bcm_tx_lock);
++ flags = op->flags;
++ count = op->count;
++ ival1 = op->ival1;
++ ival2 = op->ival2;
++ spin_unlock_bh(&op->bcm_tx_lock);
++
++ if (!count && (flags & TX_COUNTEVT)) {
+ /* create notification to user */
+ memset(&msg_head, 0, sizeof(msg_head));
+ msg_head.opcode = TX_EXPIRED;
+- msg_head.flags = op->flags;
+- msg_head.count = op->count;
+- msg_head.ival1 = op->ival1;
+- msg_head.ival2 = op->ival2;
++ msg_head.flags = flags;
++ msg_head.count = count;
++ msg_head.ival1 = ival1;
++ msg_head.ival2 = ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+ }
+
+- } else if (op->kt_ival2) {
++ } else if (tx_ival2) {
+ bcm_can_tx(op, NULL);
+ }
+
+@@ -983,6 +1011,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check the given can_id */
+ op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
+ if (op) {
++ void *new_frames;
++
+ /* update existing BCM operation */
+
+ /*
+@@ -993,11 +1023,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+- /* update CAN frames content */
++ /* get new CAN frames content into a staging buffer before
++ * locking: validate and normalize the frames there so that
++ * bcm_can_tx() / bcm_tx_timeout_handler() never observe a
++ * partially updated or unvalidated frame in op->frames
++ */
++ new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL);
++ if (!new_frames)
++ return -ENOMEM;
++
+ for (i = 0; i < msg_head->nframes; i++) {
+
+- cf = op->frames + op->cfsiz * i;
++ cf = new_frames + op->cfsiz * i;
+ err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
++ if (err < 0) {
++ kfree(new_frames);
++ return err;
++ }
+
+ if (op->flags & CAN_FD_FRAME) {
+ if (cf->len > 64)
+@@ -1007,36 +1049,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ err = -EINVAL;
+ }
+
+- if (err < 0)
++ if (err < 0) {
++ kfree(new_frames);
+ return err;
++ }
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ cf->can_id = msg_head->can_id;
+ }
+ }
++
++ spin_lock_bh(&op->bcm_tx_lock);
++
++ /* update CAN frames content */
++ memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz);
++
+ op->flags = msg_head->flags;
+
+- /* only lock for unlikely count/nframes/currframe changes */
+ if (op->nframes != msg_head->nframes ||
+- op->flags & TX_RESET_MULTI_IDX ||
+- op->flags & SETTIMER) {
+-
+- spin_lock_bh(&op->bcm_tx_lock);
++ op->flags & TX_RESET_MULTI_IDX) {
++ /* potentially update changed nframes */
++ op->nframes = msg_head->nframes;
++ /* restart multiple frame transmission */
++ op->currframe = 0;
++ }
+
+- if (op->nframes != msg_head->nframes ||
+- op->flags & TX_RESET_MULTI_IDX) {
+- /* potentially update changed nframes */
+- op->nframes = msg_head->nframes;
+- /* restart multiple frame transmission */
+- op->currframe = 0;
+- }
++ if (op->flags & SETTIMER)
++ op->count = msg_head->count;
+
+- if (op->flags & SETTIMER)
+- op->count = msg_head->count;
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+- spin_unlock_bh(&op->bcm_tx_lock);
+- }
++ kfree(new_frames);
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+@@ -1113,10 +1157,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ if (op->flags & SETTIMER) {
+ /* set timer values */
++ spin_lock_bh(&op->bcm_tx_lock);
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+ /* disable an active timer due to zero values? */
+ if (!op->kt_ival1 && !op->kt_ival2)
+--
+2.53.0
+
--- /dev/null
+From 95ec4c6e157ca8f15bc0bf64e62ae008e9a0401b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:36 +0200
+Subject: can: bcm: fix CAN frame rx/tx statistics
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 upstream.
+
+KCSAN detected a data race within the bcm_rx_handler() when two CAN frames
+have been simultaneously received and processed in a single rx op by two
+different CPUs.
+
+Use atomic operations with (signed) long data types to access the
+statistics in the hot path to fix the KCSAN complaint.
+
+Additionally simplify the update and check of statistics overflow by
+using the atomic operations in separate bcm_update_[rx|tx]_stats()
+functions. The rx variant runs under bcm_rx_update_lock to prevent
+races when resetting the two rx counters; the tx variant runs under
+bcm_tx_lock and only needs to guard its own counter's overflow.
+
+As the rx path resets its values already at LONG_MAX / 100, there is
+no conflict between the two locking domains (bcm_rx_update_lock vs.
+bcm_tx_lock) even for ops that use both paths.
+
+The rx statistics update and the frames_filtered update in
+bcm_rx_changed() were previously performed in two separate
+bcm_rx_update_lock sections. For an rx op subscribed on all interfaces
+(ifindex == 0), bcm_rx_handler() can run concurrently on different
+CPUs, so a counter reset by one CPU between these two sections could
+leave frames_filtered larger than frames_abs on another CPU, producing
+a bogus (even negative) reduction percentage in procfs. Update the
+statistics in the same critical section as bcm_rx_changed() to close
+this gap, which also removes the now unneeded extra lock/unlock pair
+around the traffic_flags calculation.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-4-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 67 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 45 insertions(+), 22 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 131c7783b92d33..5d5904076d211d 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -109,7 +109,7 @@ struct bcm_op {
+ int ifindex;
+ canid_t can_id;
+ u32 flags;
+- unsigned long frames_abs, frames_filtered;
++ atomic_long_t frames_abs, frames_filtered;
+ struct bcm_timeval ival1, ival2;
+ struct hrtimer timer, thrtimer;
+ ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
+@@ -216,10 +216,13 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+
+ list_for_each_entry_rcu(op, &bo->rx_ops, list) {
+
+- unsigned long reduction;
++ long reduction, frames_filtered, frames_abs;
++
++ frames_filtered = atomic_long_read(&op->frames_filtered);
++ frames_abs = atomic_long_read(&op->frames_abs);
+
+ /* print only active entries & prevent division by zero */
+- if (!op->frames_abs)
++ if (!frames_abs)
+ continue;
+
+ seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
+@@ -241,9 +244,9 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ (long long)ktime_to_us(op->kt_ival2));
+
+ seq_printf(m, "# recv %ld (%ld) => reduction: ",
+- op->frames_filtered, op->frames_abs);
++ frames_filtered, frames_abs);
+
+- reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
++ reduction = 100 - (frames_filtered * 100) / frames_abs;
+
+ seq_printf(m, "%s%ld%%\n",
+ (reduction == 100) ? "near " : "", reduction);
+@@ -267,7 +270,8 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ seq_printf(m, "t2=%lld ",
+ (long long)ktime_to_us(op->kt_ival2));
+
+- seq_printf(m, "# sent %ld\n", op->frames_abs);
++ seq_printf(m, "# sent %ld\n",
++ atomic_long_read(&op->frames_abs));
+ }
+ seq_putc(m, '\n');
+
+@@ -277,6 +281,24 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ }
+ #endif /* CONFIG_PROC_FS */
+
++static void bcm_update_rx_stats(struct bcm_op *op)
++{
++ /* prevent overflow of the reduction% calculation in bcm_proc_show() */
++ if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) {
++ atomic_long_set(&op->frames_filtered, 0);
++ atomic_long_set(&op->frames_abs, 0);
++ }
++}
++
++static void bcm_update_tx_stats(struct bcm_op *op)
++{
++ /* tx_op has no reduction% calculation - use the full range and
++ * just keep the displayed counter non-negative on overflow
++ */
++ if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX)
++ atomic_long_set(&op->frames_abs, 0);
++}
++
+ /*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+@@ -328,7 +350,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)
+ spin_lock_bh(&op->bcm_tx_lock);
+
+ if (!err)
+- op->frames_abs++;
++ bcm_update_tx_stats(op);
+
+ /* only advance the cyclic sequence if nothing reset currframe while
+ * we were sending - a concurrent TX_RESET_MULTI_IDX means this
+@@ -473,12 +495,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
+ {
+ struct bcm_msg_head head;
+
+- /* update statistics */
+- op->frames_filtered++;
+-
+- /* prevent statistics overflow */
+- if (op->frames_filtered > ULONG_MAX/100)
+- op->frames_filtered = op->frames_abs = 0;
++ /* update statistics (frames_filtered <= frames_abs) */
++ if (atomic_long_read(&op->frames_abs))
++ atomic_long_inc(&op->frames_filtered);
+
+ /* this element is not throttled anymore */
+ data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV);
+@@ -713,25 +732,29 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+- /* update statistics */
+- op->frames_abs++;
+
+- /* snapshot the flag under lock: op->flags/op->frames may be updated
+- * concurrently by bcm_rx_setup().
+- */
++ /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */
+ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ rtr_frame = op->flags & RX_RTR_FRAME;
+- if (rtr_frame)
++ if (rtr_frame) {
++ bcm_update_rx_stats(op);
++ /* snapshot RTR content under lock */
+ memcpy(&rtrframe, op->frames, op->cfsiz);
+- spin_unlock_bh(&op->bcm_rx_update_lock);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+- if (rtr_frame) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
+ bcm_can_tx(op, &rtrframe);
+ return;
+ }
+
+- spin_lock_bh(&op->bcm_rx_update_lock);
++ /* update statistics in the same critical section as bcm_rx_changed()
++ * below: frames_filtered must never be checked/incremented against a
++ * frames_abs snapshot from a concurrent bcm_rx_handler() call on
++ * another CPU for the same (wildcard) op, or frames_filtered can end
++ * up larger than frames_abs.
++ */
++ bcm_update_rx_stats(op);
+
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+--
+2.53.0
+
--- /dev/null
+From d882912b7ff9d73b942418c0ef1e3e5c95b0e972 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:41 +0200
+Subject: can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler()
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 upstream.
+
+For an rx op subscribed on all interfaces (ifindex == 0), the same op
+is registered once in the shared per-netns wildcard filter list, so
+bcm_rx_handler() can run concurrently on different CPUs for frames
+arriving on different net devices.
+
+op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was
+taken, allowing concurrent writers to race each other - including a torn
+store of the 64-bit rx_stamp on 32-bit platforms.
+
+Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex
+of the very same frame whose content it is delivering. So the assignment
+is placed in the same unbroken bcm_rx_update_lock section as the content
+comparison.
+
+As a side effect, the RTR-request frame feature (which never reach
+bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only
+the notification path needs them.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707145135.5BC831F00A3A@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-10-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 3c4abb6397bc66..93572cb59cf7a9 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -756,11 +756,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ /* disable timeout */
+ hrtimer_cancel(&op->timer);
+
+- /* save rx timestamp */
+- op->rx_stamp = skb->tstamp;
+- /* save originator for recvfrom() */
+- op->rx_ifindex = skb->dev->ifindex;
+-
+ /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */
+ spin_lock_bh(&op->bcm_rx_update_lock);
+
+@@ -784,6 +779,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ */
+ bcm_update_rx_stats(op);
+
++ /* save rx timestamp and originator for recvfrom() under lock.
++ * For an op subscribed on all interfaces (ifindex == 0)
++ * bcm_rx_handler() can run concurrently on different CPUs so
++ * the CAN content and the meta data must be bundled correctly.
++ */
++ op->rx_stamp = skb->tstamp;
++ op->rx_ifindex = skb->dev->ifindex;
++
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ bcm_rx_update_and_send(op, op->last_frames, rxframe);
+--
+2.53.0
+
--- /dev/null
+From 99f31f2addae169c087376c48e3505720ecceaed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:40 +0200
+Subject: can: bcm: fix stale rx/tx ops after device removal
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 3b762c0d950383ab7a002686c9136b9aa55d2d70 upstream.
+
+RX: an RX_SETUP update(!) for an existing op skipped can_rx_register()
+unconditionally, even when a concurrent NETDEV_UNREGISTER had already
+torn down its registration (op->rx_reg_dev == NULL). This silently
+did not re-enable frame delivery for that updated filter. bcm_rx_setup()
+now re-registers in that case, while leaving rx_ops with ifindex = 0
+(all CAN devices) which never carry a tracked rx_reg_dev registered as-is.
+
+TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving
+tx_ops with an active cyclic transmission re-arming its hrtimer
+indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer
+prevents the runaway timer and any injection into a later reused ifindex,
+since nothing else calls bcm_can_tx() for the op until an explicit
+TX_SETUP update re-arms it.
+
+Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops,
+the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup()
+always rejects ifindex 0, so clearing it would strand the op: neither a
+later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could
+ever find it again, since both require an exact ifindex match.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260708094536.DDF821F00A3A@smtp.kernel.org/
+Closes: https://lore.kernel.org/linux-can/20260708154039.347ED1F000E9@smtp.kernel.org/
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-9-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 44 insertions(+), 10 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 59fc7cee12f41f..3c4abb6397bc66 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1234,6 +1234,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int do_rx_register;
++ int new_op = 0;
+ int err = 0;
+
+ if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+@@ -1318,8 +1319,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* free temporary frames / kfree(NULL) is safe */
+ kfree(new_frames);
+
+- /* Only an update -> do not call can_rx_register() */
+- do_rx_register = 0;
++ /* Don't register a new CAN filter for the rx_op update unless
++ * a concurrent NETDEV_UNREGISTER notifier already tore down
++ * the previous registration. In this case the receiver needs
++ * to be re-registered here so that this update doesn't
++ * silently stop delivering frames for the given ifindex.
++ * Ops with ifindex = 0 (all CAN interfaces) never carry a
++ * tracked rx_reg_dev and stay registered as-is.
++ */
++ do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0;
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+@@ -1389,6 +1397,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* call can_rx_register() */
+ do_rx_register = 1;
++ new_op = 1;
+
+ } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+@@ -1402,7 +1411,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (op->flags & SETTIMER) {
+
+ /* set timers (locked) for newly created op */
+- if (do_rx_register) {
++ if (new_op) {
+ spin_lock_bh(&op->bcm_rx_update_lock);
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+@@ -1432,7 +1441,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ HRTIMER_MODE_REL_SOFT);
+ }
+
+- /* now we can register for can_ids, if we added a new bcm_op */
++ /* now we can register for can_ids, if we added a new bcm_op
++ * or need to re-register after a NETDEV_UNREGISTER tore down
++ * the previous registration of an existing op
++ */
+ if (do_rx_register) {
+ if (ifindex) {
+ struct net_device *dev;
+@@ -1462,18 +1474,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ err = -ENODEV;
+ }
+
+- } else
++ } else {
+ err = can_rx_register(sock_net(sk), NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op, "bcm", sk);
++ }
++
+ if (err) {
+- /* this bcm rx op is broken -> remove it */
+- bcm_remove_op(op);
++ /* newly created bcm rx op is broken -> remove it */
++ if (new_op) {
++ bcm_remove_op(op);
++ return err;
++ }
++
++ /* an existing op just stays unregistered.
++ * Cancel op->timer and (defensively) op->thrtimer.
++ * Other settings can't be reached until the next
++ * successful RX_SETUP.
++ */
++ hrtimer_cancel(&op->timer);
++ hrtimer_cancel(&op->thrtimer);
+ return err;
+ }
+
+- /* add this bcm_op to the list of the rx_ops */
+- list_add_rcu(&op->list, &bo->rx_ops);
++ /* add a new bcm_op to the list of the rx_ops */
++ if (new_op)
++ list_add_rcu(&op->list, &bo->rx_ops);
+ }
+
+ return msg_head->nframes * op->cfsiz + MHSIZ;
+@@ -1689,11 +1715,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+- /* remove device specific receive entries */
++ /* rx_ops: remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list)
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
++ /* tx_ops: stop device specific cyclic transmissions on the
++ * vanishing ifindex. Cancelling the timer is enough to stop
++ * cyclic bcm_can_tx() calls as there is no re-arming.
++ */
++ list_for_each_entry(op, &bo->tx_ops, list)
++ if (op->ifindex == dev->ifindex)
++ hrtimer_cancel(&op->timer);
++
+ /* remove device reference, if this is our bound device */
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ #if IS_ENABLED(CONFIG_PROC_FS)
+--
+2.53.0
+
--- /dev/null
+From 81ab3bee66367e82ce05042c398f119e69b55df1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:42 +0200
+Subject: can: bcm: track a single source interface for ANYDEV timeout/throttle
+ ops
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 2f5976f54a04e9f18b25283036ac3136be453b17 upstream.
+
+An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or
+throttle timer has no defined semantics when matching frames arrive
+from several interfaces: bcm_rx_handler() can run concurrently for
+the same op on different CPUs, racing hrtimer_cancel()/
+bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing
+spurious RX_TIMEOUT notifications and last_frames corruption. The
+same concurrency lets throttled multiplex frames from different
+interfaces clobber the single rx_ifindex/rx_stamp fields shared by
+the op.
+
+Add op->if_detected to track the first interface that delivers a
+matching frame while a timeout/throttle timer is configured, and
+reject frames from any other interface for that op. The claim is
+decided in bcm_rx_handler() before hrtimer_cancel() touches
+op->timer, so a rejected frame can never disturb the claimed
+interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME,
+independent of kt_ival1/kt_ival2, since those may briefly hold a
+stale value from an earlier non-RTR configuration.
+
+The claim is released in bcm_notify() on NETDEV_UNREGISTER and in
+bcm_rx_setup() when SETTIMER reconfigures the timer values.
+
+A (re-)claim is only possible on CAN devices in NETREG_REGISTERED
+dev->reg_state to cover the release in bcm_notify() where reg_state
+becomes NETREG_UNREGISTERING until synchronize_net().
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260709105031.1A39C1F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-11-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 5 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 93572cb59cf7a9..f655764aae83ef 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -114,6 +114,7 @@ struct bcm_op {
+ struct hrtimer timer, thrtimer;
+ ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
+ int rx_ifindex;
++ int if_detected; /* first received ifindex in ANYDEV rx_op mode */
+ int cfsiz;
+ u32 count;
+ u32 nframes;
+@@ -753,6 +754,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ if (skb->len != op->cfsiz)
+ return;
+
++ /* An ANYDEV op with an active RX timeout and/or throttle timer
++ * tracks a single source interface: claim the first interface that
++ * delivers a matching frame and reject frames from any other one,
++ * before hrtimer_cancel() below can touch op->timer - this avoids
++ * racing bcm_rx_timeout_handler() across concurrent interfaces.
++ * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly
++ * hold a stale value from an earlier non-RTR configuration.
++ */
++ if (!op->ifindex) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
++ if (!(op->flags & RX_RTR_FRAME) &&
++ (op->kt_ival1 || op->kt_ival2)) {
++ /* don't claim to vanishing interface */
++ if (!op->if_detected &&
++ skb->dev->reg_state == NETREG_REGISTERED)
++ op->if_detected = skb->dev->ifindex;
++
++ if (op->if_detected != skb->dev->ifindex) {
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ return;
++ }
++ }
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
++
+ /* disable timeout */
+ hrtimer_cancel(&op->timer);
+
+@@ -779,10 +807,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ */
+ bcm_update_rx_stats(op);
+
+- /* save rx timestamp and originator for recvfrom() under lock.
+- * For an op subscribed on all interfaces (ifindex == 0)
+- * bcm_rx_handler() can run concurrently on different CPUs so
+- * the CAN content and the meta data must be bundled correctly.
++ /* save rx timestamp and originator for recvfrom() under lock: an
++ * ANYDEV op without an active timer can still run concurrently on
++ * different CPUs, so content and meta data must be bundled here.
+ */
+ op->rx_stamp = skb->tstamp;
+ op->rx_ifindex = skb->dev->ifindex;
+@@ -1316,6 +1343,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ op->kt_lastmsg = 0;
++ op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */
+ }
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+@@ -1719,10 +1747,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
+ lock_sock(sk);
+
+ /* rx_ops: remove device specific receive entries */
+- list_for_each_entry(op, &bo->rx_ops, list)
++ list_for_each_entry(op, &bo->rx_ops, list) {
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
++ /* release an ANYDEV op's claim (see bcm_rx_handler())
++ * on this now confirmed-gone interface.
++ */
++ if (!op->ifindex) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ if (op->if_detected == dev->ifindex)
++ op->if_detected = 0;
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
++ }
++
+ /* tx_ops: stop device specific cyclic transmissions on the
+ * vanishing ifindex. Cancelling the timer is enough to stop
+ * cyclic bcm_can_tx() calls as there is no re-arming.
+--
+2.53.0
+
--- /dev/null
+From 52c04ac78d9bf458516fb6ab6442dbb861473205 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:13:38 +0200
+Subject: can: bcm: validate frame length in bcm_rx_setup() for RTR replies
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 62ec41f364648be79d54d94d0d240ee326948afd upstream.
+
+bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits
+before installing frames for TX_SETUP, but bcm_rx_setup() never did
+the same for the RTR-reply frame configured via RX_SETUP with
+RX_RTR_FRAME.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 41 insertions(+), 18 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 93552b65c00ee4..d74c5d1e29358f 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1194,22 +1194,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
+-static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
+- struct bcm_op *op, void *new_frames)
++static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
++ struct bcm_op *op, void *new_frames)
+ {
++ struct canfd_frame *frame0 = new_frames;
++
++ if (!(msg_head->flags & RX_RTR_FRAME))
++ return 0;
++
++ /* this frame is sent out as-is by bcm_can_tx() whenever a matching
++ * remote request is received, so validate its length the same way
++ * bcm_tx_setup() validates TX_SETUP frames before installing it
++ */
++ if (msg_head->flags & CAN_FD_FRAME) {
++ if (frame0->len > 64)
++ return -EINVAL;
++ } else {
++ if (frame0->len > 8)
++ return -EINVAL;
++ }
++
+ /* funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ * normalize this on the staged buffer, before it is
+ * ever installed into op->frames.
+ */
+- if (msg_head->flags & RX_RTR_FRAME) {
+- struct canfd_frame *frame0 = new_frames;
++ if ((msg_head->flags & TX_CP_CAN_ID) ||
++ frame0->can_id == op->can_id)
++ frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+
+- if ((msg_head->flags & TX_CP_CAN_ID) ||
+- frame0->can_id == op->can_id)
+- frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+- }
++ return 0;
+ }
+
+ /*
+@@ -1272,7 +1287,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
+- bcm_rx_setup_rtr_check(msg_head, op, new_frames);
++ err = bcm_rx_setup_rtr_check(msg_head, op, new_frames);
++ if (err < 0) {
++ kfree(new_frames);
++ return err;
++ }
+ }
+
+ spin_lock_bh(&op->bcm_rx_update_lock);
+@@ -1345,16 +1364,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (msg_head->nframes) {
+ err = memcpy_from_msg(op->frames, msg,
+ msg_head->nframes * op->cfsiz);
+- if (err < 0) {
+- if (op->frames != &op->sframe)
+- kfree(op->frames);
+- if (op->last_frames != &op->last_sframe)
+- kfree(op->last_frames);
+- kfree(op);
+- return err;
+- }
++ if (err < 0)
++ goto free_op;
+
+- bcm_rx_setup_rtr_check(msg_head, op, op->frames);
++ err = bcm_rx_setup_rtr_check(msg_head, op, op->frames);
++ if (err < 0)
++ goto free_op;
+ }
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+@@ -1456,6 +1471,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ }
+
+ return msg_head->nframes * op->cfsiz + MHSIZ;
++
++free_op:
++ if (op->frames != &op->sframe)
++ kfree(op->frames);
++ if (op->last_frames != &op->last_sframe)
++ kfree(op->last_frames);
++ kfree(op);
++ return err;
+ }
+
+ /*
+--
+2.53.0
+
raw-use-more-conventional-iterators.patch
net-ipv6-fix-dif-and-sdif-mismatch-in-raw6_icmp_erro.patch
bpf-sockmap-fix-cork-use-after-free-in-tcp_bpf_sendm.patch
+can-bcm-defer-rx_op-deallocation-to-workqueue-to-fix.patch
+can-bcm-add-locking-when-updating-filter-and-timer-v.patch
+can-bcm-fix-can-frame-rx-tx-statistics.patch
+can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
+can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
+can-bcm-add-missing-device-refcount-for-can-filter-r.patch
+can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
+can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
+can-bcm-track-a-single-source-interface-for-anydev-t.patch
--- /dev/null
+From c4af7dcbccbd407527f7fe82b813f2889531d396 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:42 +0200
+Subject: can: bcm: add locking when updating filter and timer values
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 749179c2e25b95d22499ed29096b3e02d6dfd2b4 upstream.
+
+KCSAN detected a simultaneous access to timer values that can be
+overwritten in bcm_rx_setup() when updating timer and filter content
+while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler()
+run concurrently on incoming CAN traffic.
+
+Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter
+(nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new
+per-op bcm_rx_update_lock, taken with the matching scope in the RX
+handlers. memcpy_from_msg() is staged into a temporary buffer before the
+lock is taken, since it can sleep and must not run under a spinlock.
+
+hrtimer_cancel() is always called without bcm_rx_update_lock held, since
+bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a
+running callback would otherwise deadlock against the canceller.
+
+Also close a related race: bcm_rx_setup() cleared the RTR flag in the
+stored reply frame's can_id as a separate, unprotected step after the
+frame content was already installed, so a concurrent bcm_rx_handler()
+could transmit a stale reply with CAN_RTR_FLAG still set. Fold that
+normalization into the initial frame preparation instead (on the staged
+buffer for updates, directly on op->frames pre-registration for new
+ops), so the installed frame is always atomically self-consistent.
+
+bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected
+snapshot of op->flags before deciding whether to call bcm_can_tx(),
+but does not hold the lock across that call.
+
+Also take a lock-protected snapshot of the currframe in bcm_can_tx()
+to avoid partly overwrites by content updates in bcm_tx_setup().
+Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset
+op->currframe between the two locked sections in bcm_can_tx().
+
+Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler().
+kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it
+cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock.
+
+Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates")
+Reported-by: syzbot+75e5e4ae00c3b4bb544e@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/linux-can/6975d5cf.a00a0220.33ccc7.0022.GAE@google.com/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-3-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 178 +++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 134 insertions(+), 44 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index ca0f5d0c4e9ba3..f62361d9719f56 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -126,6 +126,7 @@ struct bcm_op {
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+ spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
++ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
+ };
+
+ struct bcm_sock {
+@@ -280,21 +281,27 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+ */
+-static void bcm_can_tx(struct bcm_op *op)
++static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)
+ {
+ struct sk_buff *skb;
+ struct net_device *dev;
+- struct canfd_frame *cf;
++ struct canfd_frame cframe;
++ bool cyclic = !cf;
++ unsigned int idx = 0;
+ int err;
+
+ /* no target device? => exit */
+ if (!op->ifindex)
+ return;
+
+- /* read currframe under lock protection */
+- spin_lock_bh(&op->bcm_tx_lock);
+- cf = op->frames + op->cfsiz * op->currframe;
+- spin_unlock_bh(&op->bcm_tx_lock);
++ if (cyclic) {
++ /* read currframe under lock protection */
++ spin_lock_bh(&op->bcm_tx_lock);
++ idx = op->currframe;
++ memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz);
++ cf = &cframe;
++ spin_unlock_bh(&op->bcm_tx_lock);
++ }
+
+ dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
+ if (!dev) {
+@@ -323,14 +330,20 @@ static void bcm_can_tx(struct bcm_op *op)
+ if (!err)
+ op->frames_abs++;
+
+- op->currframe++;
++ /* only advance the cyclic sequence if nothing reset currframe while
++ * we were sending - a concurrent TX_RESET_MULTI_IDX means this
++ * frame's bookkeeping belongs to a sequence that no longer exists
++ */
++ if (!cyclic || op->currframe == idx) {
++ op->currframe++;
+
+- /* reached last frame? */
+- if (op->currframe >= op->nframes)
+- op->currframe = 0;
++ /* reached last frame? */
++ if (op->currframe >= op->nframes)
++ op->currframe = 0;
+
+- if (op->count > 0)
+- op->count--;
++ if (op->count > 0)
++ op->count--;
++ }
+
+ spin_unlock_bh(&op->bcm_tx_lock);
+ out:
+@@ -429,7 +442,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ struct bcm_msg_head msg_head;
+
+ if (op->kt_ival1 && (op->count > 0)) {
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+ if (!op->count && (op->flags & TX_COUNTEVT)) {
+
+ /* create notification to user */
+@@ -446,7 +459,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ }
+
+ } else if (op->kt_ival2) {
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+ }
+
+ return bcm_tx_set_expiry(op, &op->timer) ?
+@@ -585,6 +598,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+ struct bcm_msg_head msg_head;
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+ /* clear received CAN frames to indicate 'nothing received' */
+@@ -601,6 +616,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+
+ return HRTIMER_NORESTART;
+@@ -649,15 +666,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op)
+ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
+ {
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
++ enum hrtimer_restart ret;
+
+- if (bcm_rx_thr_flush(op)) {
+- hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+- return HRTIMER_RESTART;
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
++ /* kt_ival2 may have been concurrently cleared by bcm_rx_setup()
++ * before it cancels this timer - never forward with a zero
++ * interval in that case.
++ */
++ if (bcm_rx_thr_flush(op) && op->kt_ival2) {
++ hrtimer_forward_now(hrtimer, op->kt_ival2);
++ ret = HRTIMER_RESTART;
+ } else {
+ /* rearm throttle handling */
+ op->kt_lastmsg = 0;
+- return HRTIMER_NORESTART;
++ ret = HRTIMER_NORESTART;
+ }
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ return ret;
+ }
+
+ /*
+@@ -667,7 +695,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ {
+ struct bcm_op *op = (struct bcm_op *)data;
+ const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
++ struct canfd_frame rtrframe;
+ unsigned int i;
++ bool rtr_frame;
+
+ if (op->can_id != rxframe->can_id)
+ return;
+@@ -686,12 +716,23 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ /* update statistics */
+ op->frames_abs++;
+
+- if (op->flags & RX_RTR_FRAME) {
++ /* snapshot the flag under lock: op->flags/op->frames may be updated
++ * concurrently by bcm_rx_setup().
++ */
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ rtr_frame = op->flags & RX_RTR_FRAME;
++ if (rtr_frame)
++ memcpy(&rtrframe, op->frames, op->cfsiz);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ if (rtr_frame) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
+- bcm_can_tx(op);
++ bcm_can_tx(op, &rtrframe);
+ return;
+ }
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ bcm_rx_update_and_send(op, op->last_frames, rxframe);
+@@ -725,6 +766,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+
+ rx_starttimer:
+ bcm_rx_starttimer(op);
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+
+ /*
+@@ -1068,7 +1111,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ list_add_rcu(&op->list, &bo->tx_ops);
+
+ if (op->flags & TX_ANNOUNCE)
+- bcm_can_tx(op);
++ bcm_can_tx(op, NULL);
+
+ if (op->flags & STARTTIMER)
+ bcm_tx_start_timer(op);
+@@ -1082,6 +1125,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
++static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
++ struct bcm_op *op, void *new_frames)
++{
++ /* funny feature in RX(!)_SETUP only for RTR-mode:
++ * copy can_id into frame BUT without RTR-flag to
++ * prevent a full-load-loopback-test ... ;-]
++ * normalize this on the staged buffer, before it is
++ * ever installed into op->frames.
++ */
++ if (msg_head->flags & RX_RTR_FRAME) {
++ struct canfd_frame *frame0 = new_frames;
++
++ if ((msg_head->flags & TX_CP_CAN_ID) ||
++ frame0->can_id == op->can_id)
++ frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
++ }
++}
++
+ /*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+@@ -1116,6 +1177,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check the given can_id */
+ op = bcm_find_op(&bo->rx_ops, msg_head, ifindex);
+ if (op) {
++ void *new_frames = NULL;
++
+ /* update existing BCM operation */
+
+ /*
+@@ -1127,19 +1190,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return -E2BIG;
+
+ if (msg_head->nframes) {
+- /* update CAN frames content */
+- err = memcpy_from_msg(op->frames, msg,
++ /* get new CAN frames content before locking */
++ new_frames = kmalloc(msg_head->nframes * op->cfsiz,
++ GFP_KERNEL);
++ if (!new_frames)
++ return -ENOMEM;
++
++ err = memcpy_from_msg(new_frames, msg,
+ msg_head->nframes * op->cfsiz);
+- if (err < 0)
++ if (err < 0) {
++ kfree(new_frames);
+ return err;
++ }
+
+- /* clear last_frames to indicate 'nothing received' */
+- memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
++ bcm_rx_setup_rtr_check(msg_head, op, new_frames);
+ }
+
++ spin_lock_bh(&op->bcm_rx_update_lock);
+ op->nframes = msg_head->nframes;
+ op->flags = msg_head->flags;
+
++ if (msg_head->nframes) {
++ /* update CAN frames content */
++ memcpy(op->frames, new_frames,
++ msg_head->nframes * op->cfsiz);
++
++ /* clear last_frames to indicate 'nothing received' */
++ memset(op->last_frames, 0,
++ msg_head->nframes * op->cfsiz);
++ }
++
++ if (msg_head->flags & SETTIMER) {
++ op->ival1 = msg_head->ival1;
++ op->ival2 = msg_head->ival2;
++ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
++ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ op->kt_lastmsg = 0;
++ }
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++
++ /* free temporary frames / kfree(NULL) is safe */
++ kfree(new_frames);
++
+ /* Only an update -> do not call can_rx_register() */
+ do_rx_register = 0;
+
+@@ -1150,6 +1242,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return -ENOMEM;
+
+ spin_lock_init(&op->bcm_tx_lock);
++ spin_lock_init(&op->bcm_rx_update_lock);
+ op->can_id = msg_head->can_id;
+ op->nframes = msg_head->nframes;
+ op->cfsiz = CFSIZ(msg_head->flags);
+@@ -1191,6 +1284,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ kfree(op);
+ return err;
+ }
++
++ bcm_rx_setup_rtr_check(msg_head, op, op->frames);
+ }
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+@@ -1218,29 +1313,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check flags */
+
+ if (op->flags & RX_RTR_FRAME) {
+- struct canfd_frame *frame0 = op->frames;
+-
+ /* no timers in RTR-mode */
+ hrtimer_cancel(&op->thrtimer);
+ hrtimer_cancel(&op->timer);
+-
+- /*
+- * funny feature in RX(!)_SETUP only for RTR-mode:
+- * copy can_id into frame BUT without RTR-flag to
+- * prevent a full-load-loopback-test ... ;-]
+- */
+- if ((op->flags & TX_CP_CAN_ID) ||
+- (frame0->can_id == op->can_id))
+- frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+-
+ } else {
+ if (op->flags & SETTIMER) {
+
+- /* set timer value */
+- op->ival1 = msg_head->ival1;
+- op->ival2 = msg_head->ival2;
+- op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+- op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ /* set timers (locked) for newly created op */
++ if (do_rx_register) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ op->ival1 = msg_head->ival1;
++ op->ival2 = msg_head->ival2;
++ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
++ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ op->kt_lastmsg = 0;
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
+
+ /* disable an active timer due to zero value? */
+ if (!op->kt_ival1)
+@@ -1250,9 +1338,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ * In any case cancel the throttle timer, flush
+ * potentially blocked msgs and reset throttle handling
+ */
+- op->kt_lastmsg = 0;
+ hrtimer_cancel(&op->thrtimer);
++
++ spin_lock_bh(&op->bcm_rx_update_lock);
+ bcm_rx_thr_flush(op);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+
+ if ((op->flags & STARTTIMER) && op->kt_ival1)
+--
+2.53.0
+
--- /dev/null
+From 4a97fce2809debe6a543c17ee89b74472f595ff2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:46 +0200
+Subject: can: bcm: add missing device refcount for CAN filter removal
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit d59948293ea34b6337ce2b5febab8510de70048c upstream.
+
+sashiko-bot remarked a problem with a concurrent device unregistration
+in isotp.c which also is present in the bcm.c code. A former fix for raw.c
+commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix")
+introduced a netdevice_tracker which solves the issue for bcm.c too.
+
+bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on
+dev_get_by_index(ifindex) to re-find the device for an rx_op before
+unregistering its filter. If a concurrent NETDEV_UNREGISTER has already
+unlisted the device from the ifindex table, that lookup fails and
+can_rx_unregister() is silently skipped, leaving a stale CAN filter
+pointing at the soon-to-be-freed bcm_op/socket.
+
+Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev
+from the moment the rx filter is registered in bcm_rx_setup() until it
+is unregistered in bcm_rx_unreg(), and use that reference directly in
+bcm_release() and bcm_delete_rx_op() instead of re-looking the device
+up by ifindex.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://sashiko.dev/#/patchset/20260707094716.63578-1-socketcan@hartkopp.net
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-8-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 44 ++++++++++++++++++++++++--------------------
+ 1 file changed, 24 insertions(+), 20 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index b0c20d62a32d9a..6682440585b26f 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -884,6 +884,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+
+ /* mark as removed subscription */
+ op->rx_reg_dev = NULL;
++ dev_put(dev);
+ } else
+ printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+ "mismatch %p %p\n", op->rx_reg_dev, dev);
+@@ -914,17 +915,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
+ * 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.
+ */
+- if (op->rx_reg_dev) {
+- struct net_device *dev;
+-
+- dev = dev_get_by_index(sock_net(op->sk),
+- op->ifindex);
+- if (dev) {
+- bcm_rx_unreg(dev, op);
+- dev_put(dev);
+- }
+- }
++ if (op->rx_reg_dev)
++ bcm_rx_unreg(op->rx_reg_dev, op);
+ } else
+ can_rx_unregister(sock_net(op->sk), NULL,
+ op->can_id,
+@@ -1447,7 +1445,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ bcm_rx_handler, op,
+ "bcm", sk);
+
+- op->rx_reg_dev = dev;
++ /* keep a reference so that a later
++ * unregister can safely reach the device even
++ * if a concurrent NETDEV_UNREGISTER has
++ * already unlisted it by ifindex
++ */
++ if (!err) {
++ op->rx_reg_dev = dev;
++ dev_hold(dev);
++ }
+ dev_put(dev);
+ } else {
+ /* the requested device is gone - do not
+@@ -1820,16 +1826,14 @@ static int bcm_release(struct socket *sock)
+ * 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.
+ */
+- if (op->rx_reg_dev) {
+- struct net_device *dev;
+-
+- dev = dev_get_by_index(net, op->ifindex);
+- if (dev) {
+- bcm_rx_unreg(dev, op);
+- dev_put(dev);
+- }
+- }
++ if (op->rx_reg_dev)
++ bcm_rx_unreg(op->rx_reg_dev, op);
+ } else
+ can_rx_unregister(net, NULL, op->can_id,
+ REGMASK(op->can_id),
+--
+2.53.0
+
--- /dev/null
+From 2027827c836f1c9e02a709fd4e6ea1597260c2c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 18:39:44 +0200
+Subject: can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF
+
+From: Lee Jones <lee@kernel.org>
+
+commit 68973f9db76144825e4f35dfdc80fb8279eb2d57 upstream.
+
+Commit f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly
+synchronize_rcu()") replaced synchronize_rcu() in bcm_delete_rx_op()
+with call_rcu() and introduced the RX_NO_AUTOTIMER flag.
+
+However, this flag check was omitted for thrtimer in the packet rx
+fast-path. During BCM RX operation teardown, a concurrent RCU reader
+(bcm_rx_handler) can race and re-arm thrtimer via
+bcm_rx_update_and_send() after call_rcu() has been scheduled. Once
+the RCU grace period elapses, bcm_op is freed. The subsequently
+firing thrtimer then dereferences the deallocated op, causing a UAF.
+
+Adding flag checks to the rx fast-path (bcm_rx_update_and_send) does not
+fully close the TOCTOU race and introduces latency for every CAN frame.
+Conversely, calling hrtimer_cancel() directly inside the RCU callback
+(softirq context) is fatal as hrtimer_cancel() can sleep, triggering
+a "scheduling while atomic" panic.
+
+Resolve this by deferring the timer cancellation and memory free to a
+dedicated unbound workqueue (bcm_wq). The RCU callback now queues a
+work item to bcm_wq, which safely cancels both timers and deallocates
+memory in sleepable process context. A dedicated workqueue is used to
+prevent system-wide WQ saturation and is cleanly flushed/destroyed
+on module unload to avoid rmmod page faults.
+
+Since the deferred work can now outlive the calling context by an
+unbounded amount, also take a reference on op->sk when it is assigned
+and drop it only once the deferred work has cancelled both timers, so a
+socket can no longer be freed out from under a still-armed timer whose
+callback (bcm_send_to_user()) dereferences op->sk.
+
+Fixes: f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()")
+Tested-by: Feng Xue <feng.xue@outlook.com>
+Tested-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-1-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 34 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 317cd4c415ba28..ca0f5d0c4e9ba3 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -58,6 +58,7 @@
+ #include <linux/can/skb.h>
+ #include <linux/can/bcm.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
+ #include <linux/spinlock.h>
+ #include <net/sock.h>
+ #include <net/net_namespace.h>
+@@ -89,6 +90,8 @@ MODULE_ALIAS("can-proto-2");
+
+ #define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
+
++static struct workqueue_struct *bcm_wq;
++
+ /*
+ * easy access to the first 64 bit of can(fd)_frame payload. cp->data is
+ * 64 bit aligned so the offset has to be multiples of 8 which is ensured
+@@ -102,6 +105,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
+ struct bcm_op {
+ struct list_head list;
+ struct rcu_head rcu;
++ struct work_struct work;
+ int ifindex;
+ canid_t can_id;
+ u32 flags;
+@@ -740,9 +744,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
+ return NULL;
+ }
+
+-static void bcm_free_op_rcu(struct rcu_head *rcu_head)
++static void bcm_free_op_work(struct work_struct *work)
+ {
+- struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
++ struct bcm_op *op = container_of(work, struct bcm_op, work);
++
++ hrtimer_cancel(&op->timer);
++ hrtimer_cancel(&op->thrtimer);
+
+ if ((op->frames) && (op->frames != &op->sframe))
+ kfree(op->frames);
+@@ -750,9 +757,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+ if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+ kfree(op->last_frames);
+
++ /* the last possible access to op->timer/op->thrtimer has now
++ * happened above via hrtimer_cancel() - op->sk is no longer
++ * needed by any pending timer callback, so drop our reference
++ */
++ sock_put(op->sk);
++
+ kfree(op);
+ }
+
++static void bcm_free_op_rcu(struct rcu_head *rcu_head)
++{
++ struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
++
++ INIT_WORK(&op->work, bcm_free_op_work);
++ queue_work(bcm_wq, &op->work);
++}
++
+ static void bcm_remove_op(struct bcm_op *op)
+ {
+ hrtimer_cancel(&op->timer);
+@@ -1008,6 +1029,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
++ sock_hold(sk);
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+@@ -1173,6 +1195,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
++ sock_hold(sk);
+ op->ifindex = ifindex;
+
+ /* ifindex for timeout events w/o previous frame reception */
+@@ -1830,11 +1853,16 @@ static int __init bcm_module_init(void)
+ {
+ int err;
+
++ bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0);
++ if (!bcm_wq)
++ return -ENOMEM;
++
+ pr_info("can: broadcast manager protocol\n");
+
+ err = can_proto_register(&bcm_can_proto);
+ if (err < 0) {
+ printk(KERN_ERR "can: registration of bcm protocol failed\n");
++ destroy_workqueue(bcm_wq);
+ return err;
+ }
+
+@@ -1848,6 +1876,8 @@ static void __exit bcm_module_exit(void)
+ can_proto_unregister(&bcm_can_proto);
+ unregister_netdevice_notifier(&canbcm_notifier);
+ unregister_pernet_subsys(&canbcm_pernet_ops);
++ rcu_barrier();
++ destroy_workqueue(bcm_wq);
+ }
+
+ module_init(bcm_module_init);
+--
+2.53.0
+
--- /dev/null
+From 1bd7020c22d6a7c76cc8ee5154c29270b368cdbf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:44 +0200
+Subject: can: bcm: extend bcm_tx_lock usage for data and timer updates
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc upstream.
+
+Stage new CAN frame content for an existing tx op into a kmalloc()'d
+buffer and validate it there, mirroring the approach already used in
+bcm_rx_setup(). Only copy the validated data into op->frames while
+holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler()
+can no longer observe a partially updated or unvalidated frame.
+
+Add a missing error path for memcpy_from_msg() when copying CAN frame
+data from userspace.
+
+Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup()
+under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same
+lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the
+torn 64-bit ktime_t read on 32-bit platforms.
+
+Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 104 ++++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 75 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 596e015b35cd12..dafc5a8df4a4d0 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -125,7 +125,7 @@ struct bcm_op {
+ struct canfd_frame last_sframe;
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+- spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
++ spinlock_t bcm_tx_lock; /* protect tx data and timer updates */
+ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
+ };
+
+@@ -440,12 +440,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
+ {
+ ktime_t ival;
+
++ spin_lock_bh(&op->bcm_tx_lock);
++
+ if (op->kt_ival1 && op->count)
+ ival = op->kt_ival1;
+- else if (op->kt_ival2)
++ else if (op->kt_ival2) {
+ ival = op->kt_ival2;
+- else
++ } else {
++ spin_unlock_bh(&op->bcm_tx_lock);
+ return false;
++ }
++
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+ hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
+ return true;
+@@ -462,25 +468,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+ {
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+ struct bcm_msg_head msg_head;
++ bool tx_ival1, tx_ival2;
++
++ /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn
++ * ktime_t reads racing with concurrent bcm_tx_setup() updates
++ */
++ spin_lock_bh(&op->bcm_tx_lock);
++ tx_ival1 = op->kt_ival1 && (op->count > 0);
++ tx_ival2 = !!op->kt_ival2;
++ spin_unlock_bh(&op->bcm_tx_lock);
++
++ if (tx_ival1) {
++ u32 flags, count;
++ struct bcm_timeval ival1, ival2;
+
+- if (op->kt_ival1 && (op->count > 0)) {
+ bcm_can_tx(op, NULL);
+- if (!op->count && (op->flags & TX_COUNTEVT)) {
+
++ /* snapshot variables under lock to avoid torn reads racing
++ * with concurrent bcm_tx_setup() updates
++ */
++ spin_lock_bh(&op->bcm_tx_lock);
++ flags = op->flags;
++ count = op->count;
++ ival1 = op->ival1;
++ ival2 = op->ival2;
++ spin_unlock_bh(&op->bcm_tx_lock);
++
++ if (!count && (flags & TX_COUNTEVT)) {
+ /* create notification to user */
+ memset(&msg_head, 0, sizeof(msg_head));
+ msg_head.opcode = TX_EXPIRED;
+- msg_head.flags = op->flags;
+- msg_head.count = op->count;
+- msg_head.ival1 = op->ival1;
+- msg_head.ival2 = op->ival2;
++ msg_head.flags = flags;
++ msg_head.count = count;
++ msg_head.ival1 = ival1;
++ msg_head.ival2 = ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+ }
+
+- } else if (op->kt_ival2) {
++ } else if (tx_ival2) {
+ bcm_can_tx(op, NULL);
+ }
+
+@@ -983,6 +1011,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* check the given can_id */
+ op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
+ if (op) {
++ void *new_frames;
++
+ /* update existing BCM operation */
+
+ /*
+@@ -993,11 +1023,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+- /* update CAN frames content */
++ /* get new CAN frames content into a staging buffer before
++ * locking: validate and normalize the frames there so that
++ * bcm_can_tx() / bcm_tx_timeout_handler() never observe a
++ * partially updated or unvalidated frame in op->frames
++ */
++ new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL);
++ if (!new_frames)
++ return -ENOMEM;
++
+ for (i = 0; i < msg_head->nframes; i++) {
+
+- cf = op->frames + op->cfsiz * i;
++ cf = new_frames + op->cfsiz * i;
+ err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
++ if (err < 0) {
++ kfree(new_frames);
++ return err;
++ }
+
+ if (op->flags & CAN_FD_FRAME) {
+ if (cf->len > 64)
+@@ -1007,36 +1049,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ err = -EINVAL;
+ }
+
+- if (err < 0)
++ if (err < 0) {
++ kfree(new_frames);
+ return err;
++ }
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ cf->can_id = msg_head->can_id;
+ }
+ }
++
++ spin_lock_bh(&op->bcm_tx_lock);
++
++ /* update CAN frames content */
++ memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz);
++
+ op->flags = msg_head->flags;
+
+- /* only lock for unlikely count/nframes/currframe changes */
+ if (op->nframes != msg_head->nframes ||
+- op->flags & TX_RESET_MULTI_IDX ||
+- op->flags & SETTIMER) {
+-
+- spin_lock_bh(&op->bcm_tx_lock);
++ op->flags & TX_RESET_MULTI_IDX) {
++ /* potentially update changed nframes */
++ op->nframes = msg_head->nframes;
++ /* restart multiple frame transmission */
++ op->currframe = 0;
++ }
+
+- if (op->nframes != msg_head->nframes ||
+- op->flags & TX_RESET_MULTI_IDX) {
+- /* potentially update changed nframes */
+- op->nframes = msg_head->nframes;
+- /* restart multiple frame transmission */
+- op->currframe = 0;
+- }
++ if (op->flags & SETTIMER)
++ op->count = msg_head->count;
+
+- if (op->flags & SETTIMER)
+- op->count = msg_head->count;
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+- spin_unlock_bh(&op->bcm_tx_lock);
+- }
++ kfree(new_frames);
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+@@ -1113,10 +1157,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ if (op->flags & SETTIMER) {
+ /* set timer values */
++ spin_lock_bh(&op->bcm_tx_lock);
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
++ spin_unlock_bh(&op->bcm_tx_lock);
+
+ /* disable an active timer due to zero values? */
+ if (!op->kt_ival1 && !op->kt_ival2)
+--
+2.53.0
+
--- /dev/null
+From 7f71c2ea9b3cf7cce7dee53f905546ac18a27b1b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:43 +0200
+Subject: can: bcm: fix CAN frame rx/tx statistics
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 upstream.
+
+KCSAN detected a data race within the bcm_rx_handler() when two CAN frames
+have been simultaneously received and processed in a single rx op by two
+different CPUs.
+
+Use atomic operations with (signed) long data types to access the
+statistics in the hot path to fix the KCSAN complaint.
+
+Additionally simplify the update and check of statistics overflow by
+using the atomic operations in separate bcm_update_[rx|tx]_stats()
+functions. The rx variant runs under bcm_rx_update_lock to prevent
+races when resetting the two rx counters; the tx variant runs under
+bcm_tx_lock and only needs to guard its own counter's overflow.
+
+As the rx path resets its values already at LONG_MAX / 100, there is
+no conflict between the two locking domains (bcm_rx_update_lock vs.
+bcm_tx_lock) even for ops that use both paths.
+
+The rx statistics update and the frames_filtered update in
+bcm_rx_changed() were previously performed in two separate
+bcm_rx_update_lock sections. For an rx op subscribed on all interfaces
+(ifindex == 0), bcm_rx_handler() can run concurrently on different
+CPUs, so a counter reset by one CPU between these two sections could
+leave frames_filtered larger than frames_abs on another CPU, producing
+a bogus (even negative) reduction percentage in procfs. Update the
+statistics in the same critical section as bcm_rx_changed() to close
+this gap, which also removes the now unneeded extra lock/unlock pair
+around the traffic_flags calculation.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-4-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 67 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 45 insertions(+), 22 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index f62361d9719f56..596e015b35cd12 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -109,7 +109,7 @@ struct bcm_op {
+ int ifindex;
+ canid_t can_id;
+ u32 flags;
+- unsigned long frames_abs, frames_filtered;
++ atomic_long_t frames_abs, frames_filtered;
+ struct bcm_timeval ival1, ival2;
+ struct hrtimer timer, thrtimer;
+ ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
+@@ -216,10 +216,13 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+
+ list_for_each_entry_rcu(op, &bo->rx_ops, list) {
+
+- unsigned long reduction;
++ long reduction, frames_filtered, frames_abs;
++
++ frames_filtered = atomic_long_read(&op->frames_filtered);
++ frames_abs = atomic_long_read(&op->frames_abs);
+
+ /* print only active entries & prevent division by zero */
+- if (!op->frames_abs)
++ if (!frames_abs)
+ continue;
+
+ seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
+@@ -241,9 +244,9 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ (long long)ktime_to_us(op->kt_ival2));
+
+ seq_printf(m, "# recv %ld (%ld) => reduction: ",
+- op->frames_filtered, op->frames_abs);
++ frames_filtered, frames_abs);
+
+- reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
++ reduction = 100 - (frames_filtered * 100) / frames_abs;
+
+ seq_printf(m, "%s%ld%%\n",
+ (reduction == 100) ? "near " : "", reduction);
+@@ -267,7 +270,8 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ seq_printf(m, "t2=%lld ",
+ (long long)ktime_to_us(op->kt_ival2));
+
+- seq_printf(m, "# sent %ld\n", op->frames_abs);
++ seq_printf(m, "# sent %ld\n",
++ atomic_long_read(&op->frames_abs));
+ }
+ seq_putc(m, '\n');
+
+@@ -277,6 +281,24 @@ static int bcm_proc_show(struct seq_file *m, void *v)
+ }
+ #endif /* CONFIG_PROC_FS */
+
++static void bcm_update_rx_stats(struct bcm_op *op)
++{
++ /* prevent overflow of the reduction% calculation in bcm_proc_show() */
++ if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) {
++ atomic_long_set(&op->frames_filtered, 0);
++ atomic_long_set(&op->frames_abs, 0);
++ }
++}
++
++static void bcm_update_tx_stats(struct bcm_op *op)
++{
++ /* tx_op has no reduction% calculation - use the full range and
++ * just keep the displayed counter non-negative on overflow
++ */
++ if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX)
++ atomic_long_set(&op->frames_abs, 0);
++}
++
+ /*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+@@ -328,7 +350,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)
+ spin_lock_bh(&op->bcm_tx_lock);
+
+ if (!err)
+- op->frames_abs++;
++ bcm_update_tx_stats(op);
+
+ /* only advance the cyclic sequence if nothing reset currframe while
+ * we were sending - a concurrent TX_RESET_MULTI_IDX means this
+@@ -473,12 +495,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
+ {
+ struct bcm_msg_head head;
+
+- /* update statistics */
+- op->frames_filtered++;
+-
+- /* prevent statistics overflow */
+- if (op->frames_filtered > ULONG_MAX/100)
+- op->frames_filtered = op->frames_abs = 0;
++ /* update statistics (frames_filtered <= frames_abs) */
++ if (atomic_long_read(&op->frames_abs))
++ atomic_long_inc(&op->frames_filtered);
+
+ /* this element is not throttled anymore */
+ data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV);
+@@ -713,25 +732,29 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+- /* update statistics */
+- op->frames_abs++;
+
+- /* snapshot the flag under lock: op->flags/op->frames may be updated
+- * concurrently by bcm_rx_setup().
+- */
++ /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */
+ spin_lock_bh(&op->bcm_rx_update_lock);
++
+ rtr_frame = op->flags & RX_RTR_FRAME;
+- if (rtr_frame)
++ if (rtr_frame) {
++ bcm_update_rx_stats(op);
++ /* snapshot RTR content under lock */
+ memcpy(&rtrframe, op->frames, op->cfsiz);
+- spin_unlock_bh(&op->bcm_rx_update_lock);
++ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+- if (rtr_frame) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
+ bcm_can_tx(op, &rtrframe);
+ return;
+ }
+
+- spin_lock_bh(&op->bcm_rx_update_lock);
++ /* update statistics in the same critical section as bcm_rx_changed()
++ * below: frames_filtered must never be checked/incremented against a
++ * frames_abs snapshot from a concurrent bcm_rx_handler() call on
++ * another CPU for the same (wildcard) op, or frames_filtered can end
++ * up larger than frames_abs.
++ */
++ bcm_update_rx_stats(op);
+
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+--
+2.53.0
+
--- /dev/null
+From b24b85bb26e2189fb07e32056f1f490ae93c2dfd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:48 +0200
+Subject: can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler()
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 upstream.
+
+For an rx op subscribed on all interfaces (ifindex == 0), the same op
+is registered once in the shared per-netns wildcard filter list, so
+bcm_rx_handler() can run concurrently on different CPUs for frames
+arriving on different net devices.
+
+op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was
+taken, allowing concurrent writers to race each other - including a torn
+store of the 64-bit rx_stamp on 32-bit platforms.
+
+Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex
+of the very same frame whose content it is delivering. So the assignment
+is placed in the same unbroken bcm_rx_update_lock section as the content
+comparison.
+
+As a side effect, the RTR-request frame feature (which never reach
+bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only
+the notification path needs them.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707145135.5BC831F00A3A@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-10-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 8d8756f39e9b56..b4255b06ff30ac 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -756,11 +756,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ /* disable timeout */
+ hrtimer_cancel(&op->timer);
+
+- /* save rx timestamp */
+- op->rx_stamp = skb->tstamp;
+- /* save originator for recvfrom() */
+- op->rx_ifindex = skb->dev->ifindex;
+-
+ /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */
+ spin_lock_bh(&op->bcm_rx_update_lock);
+
+@@ -784,6 +779,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ */
+ bcm_update_rx_stats(op);
+
++ /* save rx timestamp and originator for recvfrom() under lock.
++ * For an op subscribed on all interfaces (ifindex == 0)
++ * bcm_rx_handler() can run concurrently on different CPUs so
++ * the CAN content and the meta data must be bundled correctly.
++ */
++ op->rx_stamp = skb->tstamp;
++ op->rx_ifindex = skb->dev->ifindex;
++
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ bcm_rx_update_and_send(op, op->last_frames, rxframe);
+--
+2.53.0
+
--- /dev/null
+From 3a7d6a43fb567b61ac5abab6c5c313f78613804b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:47 +0200
+Subject: can: bcm: fix stale rx/tx ops after device removal
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 3b762c0d950383ab7a002686c9136b9aa55d2d70 upstream.
+
+RX: an RX_SETUP update(!) for an existing op skipped can_rx_register()
+unconditionally, even when a concurrent NETDEV_UNREGISTER had already
+torn down its registration (op->rx_reg_dev == NULL). This silently
+did not re-enable frame delivery for that updated filter. bcm_rx_setup()
+now re-registers in that case, while leaving rx_ops with ifindex = 0
+(all CAN devices) which never carry a tracked rx_reg_dev registered as-is.
+
+TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving
+tx_ops with an active cyclic transmission re-arming its hrtimer
+indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer
+prevents the runaway timer and any injection into a later reused ifindex,
+since nothing else calls bcm_can_tx() for the op until an explicit
+TX_SETUP update re-arms it.
+
+Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops,
+the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup()
+always rejects ifindex 0, so clearing it would strand the op: neither a
+later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could
+ever find it again, since both require an exact ifindex match.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260708094536.DDF821F00A3A@smtp.kernel.org/
+Closes: https://lore.kernel.org/linux-can/20260708154039.347ED1F000E9@smtp.kernel.org/
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-9-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 44 insertions(+), 10 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 6682440585b26f..8d8756f39e9b56 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1234,6 +1234,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int do_rx_register;
++ int new_op = 0;
+ int err = 0;
+
+ if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+@@ -1318,8 +1319,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ /* free temporary frames / kfree(NULL) is safe */
+ kfree(new_frames);
+
+- /* Only an update -> do not call can_rx_register() */
+- do_rx_register = 0;
++ /* Don't register a new CAN filter for the rx_op update unless
++ * a concurrent NETDEV_UNREGISTER notifier already tore down
++ * the previous registration. In this case the receiver needs
++ * to be re-registered here so that this update doesn't
++ * silently stop delivering frames for the given ifindex.
++ * Ops with ifindex = 0 (all CAN interfaces) never carry a
++ * tracked rx_reg_dev and stay registered as-is.
++ */
++ do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0;
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+@@ -1389,6 +1397,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+
+ /* call can_rx_register() */
+ do_rx_register = 1;
++ new_op = 1;
+
+ } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+@@ -1402,7 +1411,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (op->flags & SETTIMER) {
+
+ /* set timers (locked) for newly created op */
+- if (do_rx_register) {
++ if (new_op) {
+ spin_lock_bh(&op->bcm_rx_update_lock);
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+@@ -1432,7 +1441,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ HRTIMER_MODE_REL_SOFT);
+ }
+
+- /* now we can register for can_ids, if we added a new bcm_op */
++ /* now we can register for can_ids, if we added a new bcm_op
++ * or need to re-register after a NETDEV_UNREGISTER tore down
++ * the previous registration of an existing op
++ */
+ if (do_rx_register) {
+ if (ifindex) {
+ struct net_device *dev;
+@@ -1462,18 +1474,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ err = -ENODEV;
+ }
+
+- } else
++ } else {
+ err = can_rx_register(sock_net(sk), NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op, "bcm", sk);
++ }
++
+ if (err) {
+- /* this bcm rx op is broken -> remove it */
+- bcm_remove_op(op);
++ /* newly created bcm rx op is broken -> remove it */
++ if (new_op) {
++ bcm_remove_op(op);
++ return err;
++ }
++
++ /* an existing op just stays unregistered.
++ * Cancel op->timer and (defensively) op->thrtimer.
++ * Other settings can't be reached until the next
++ * successful RX_SETUP.
++ */
++ hrtimer_cancel(&op->timer);
++ hrtimer_cancel(&op->thrtimer);
+ return err;
+ }
+
+- /* add this bcm_op to the list of the rx_ops */
+- list_add_rcu(&op->list, &bo->rx_ops);
++ /* add a new bcm_op to the list of the rx_ops */
++ if (new_op)
++ list_add_rcu(&op->list, &bo->rx_ops);
+ }
+
+ return msg_head->nframes * op->cfsiz + MHSIZ;
+@@ -1689,11 +1715,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+- /* remove device specific receive entries */
++ /* rx_ops: remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list)
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
++ /* tx_ops: stop device specific cyclic transmissions on the
++ * vanishing ifindex. Cancelling the timer is enough to stop
++ * cyclic bcm_can_tx() calls as there is no re-arming.
++ */
++ list_for_each_entry(op, &bo->tx_ops, list)
++ if (op->ifindex == dev->ifindex)
++ hrtimer_cancel(&op->timer);
++
+ /* remove device reference, if this is our bound device */
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ #if IS_ENABLED(CONFIG_PROC_FS)
+--
+2.53.0
+
--- /dev/null
+From a1e88cb611103f09ef0e9edd90aec62d8a26c83b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:49 +0200
+Subject: can: bcm: track a single source interface for ANYDEV timeout/throttle
+ ops
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 2f5976f54a04e9f18b25283036ac3136be453b17 upstream.
+
+An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or
+throttle timer has no defined semantics when matching frames arrive
+from several interfaces: bcm_rx_handler() can run concurrently for
+the same op on different CPUs, racing hrtimer_cancel()/
+bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing
+spurious RX_TIMEOUT notifications and last_frames corruption. The
+same concurrency lets throttled multiplex frames from different
+interfaces clobber the single rx_ifindex/rx_stamp fields shared by
+the op.
+
+Add op->if_detected to track the first interface that delivers a
+matching frame while a timeout/throttle timer is configured, and
+reject frames from any other interface for that op. The claim is
+decided in bcm_rx_handler() before hrtimer_cancel() touches
+op->timer, so a rejected frame can never disturb the claimed
+interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME,
+independent of kt_ival1/kt_ival2, since those may briefly hold a
+stale value from an earlier non-RTR configuration.
+
+The claim is released in bcm_notify() on NETDEV_UNREGISTER and in
+bcm_rx_setup() when SETTIMER reconfigures the timer values.
+
+A (re-)claim is only possible on CAN devices in NETREG_REGISTERED
+dev->reg_state to cover the release in bcm_notify() where reg_state
+becomes NETREG_UNREGISTERING until synchronize_net().
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260709105031.1A39C1F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-11-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 5 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index b4255b06ff30ac..2cf63940f6a9bb 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -114,6 +114,7 @@ struct bcm_op {
+ struct hrtimer timer, thrtimer;
+ ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
+ int rx_ifindex;
++ int if_detected; /* first received ifindex in ANYDEV rx_op mode */
+ int cfsiz;
+ u32 count;
+ u32 nframes;
+@@ -753,6 +754,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ if (skb->len != op->cfsiz)
+ return;
+
++ /* An ANYDEV op with an active RX timeout and/or throttle timer
++ * tracks a single source interface: claim the first interface that
++ * delivers a matching frame and reject frames from any other one,
++ * before hrtimer_cancel() below can touch op->timer - this avoids
++ * racing bcm_rx_timeout_handler() across concurrent interfaces.
++ * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly
++ * hold a stale value from an earlier non-RTR configuration.
++ */
++ if (!op->ifindex) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++
++ if (!(op->flags & RX_RTR_FRAME) &&
++ (op->kt_ival1 || op->kt_ival2)) {
++ /* don't claim to vanishing interface */
++ if (!op->if_detected &&
++ skb->dev->reg_state == NETREG_REGISTERED)
++ op->if_detected = skb->dev->ifindex;
++
++ if (op->if_detected != skb->dev->ifindex) {
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ return;
++ }
++ }
++
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
++
+ /* disable timeout */
+ hrtimer_cancel(&op->timer);
+
+@@ -779,10 +807,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+ */
+ bcm_update_rx_stats(op);
+
+- /* save rx timestamp and originator for recvfrom() under lock.
+- * For an op subscribed on all interfaces (ifindex == 0)
+- * bcm_rx_handler() can run concurrently on different CPUs so
+- * the CAN content and the meta data must be bundled correctly.
++ /* save rx timestamp and originator for recvfrom() under lock: an
++ * ANYDEV op without an active timer can still run concurrently on
++ * different CPUs, so content and meta data must be bundled here.
+ */
+ op->rx_stamp = skb->tstamp;
+ op->rx_ifindex = skb->dev->ifindex;
+@@ -1316,6 +1343,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ op->kt_lastmsg = 0;
++ op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */
+ }
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+@@ -1719,10 +1747,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
+ lock_sock(sk);
+
+ /* rx_ops: remove device specific receive entries */
+- list_for_each_entry(op, &bo->rx_ops, list)
++ list_for_each_entry(op, &bo->rx_ops, list) {
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
++ /* release an ANYDEV op's claim (see bcm_rx_handler())
++ * on this now confirmed-gone interface.
++ */
++ if (!op->ifindex) {
++ spin_lock_bh(&op->bcm_rx_update_lock);
++ if (op->if_detected == dev->ifindex)
++ op->if_detected = 0;
++ spin_unlock_bh(&op->bcm_rx_update_lock);
++ }
++ }
++
+ /* tx_ops: stop device specific cyclic transmissions on the
+ * vanishing ifindex. Cancelling the timer is enough to stop
+ * cyclic bcm_can_tx() calls as there is no re-arming.
+--
+2.53.0
+
--- /dev/null
+From da7bd2ed09734f49954740bab566c047fd2f9fc9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:21:45 +0200
+Subject: can: bcm: validate frame length in bcm_rx_setup() for RTR replies
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 62ec41f364648be79d54d94d0d240ee326948afd upstream.
+
+bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits
+before installing frames for TX_SETUP, but bcm_rx_setup() never did
+the same for the RTR-reply frame configured via RX_SETUP with
+RX_RTR_FRAME.
+
+Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net
+Cc: stable@kernel.org
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 41 insertions(+), 18 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index dafc5a8df4a4d0..b0c20d62a32d9a 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1194,22 +1194,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
+-static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
+- struct bcm_op *op, void *new_frames)
++static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
++ struct bcm_op *op, void *new_frames)
+ {
++ struct canfd_frame *frame0 = new_frames;
++
++ if (!(msg_head->flags & RX_RTR_FRAME))
++ return 0;
++
++ /* this frame is sent out as-is by bcm_can_tx() whenever a matching
++ * remote request is received, so validate its length the same way
++ * bcm_tx_setup() validates TX_SETUP frames before installing it
++ */
++ if (msg_head->flags & CAN_FD_FRAME) {
++ if (frame0->len > 64)
++ return -EINVAL;
++ } else {
++ if (frame0->len > 8)
++ return -EINVAL;
++ }
++
+ /* funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ * normalize this on the staged buffer, before it is
+ * ever installed into op->frames.
+ */
+- if (msg_head->flags & RX_RTR_FRAME) {
+- struct canfd_frame *frame0 = new_frames;
++ if ((msg_head->flags & TX_CP_CAN_ID) ||
++ frame0->can_id == op->can_id)
++ frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+
+- if ((msg_head->flags & TX_CP_CAN_ID) ||
+- frame0->can_id == op->can_id)
+- frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+- }
++ return 0;
+ }
+
+ /*
+@@ -1272,7 +1287,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ return err;
+ }
+
+- bcm_rx_setup_rtr_check(msg_head, op, new_frames);
++ err = bcm_rx_setup_rtr_check(msg_head, op, new_frames);
++ if (err < 0) {
++ kfree(new_frames);
++ return err;
++ }
+ }
+
+ spin_lock_bh(&op->bcm_rx_update_lock);
+@@ -1345,16 +1364,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ if (msg_head->nframes) {
+ err = memcpy_from_msg(op->frames, msg,
+ msg_head->nframes * op->cfsiz);
+- if (err < 0) {
+- if (op->frames != &op->sframe)
+- kfree(op->frames);
+- if (op->last_frames != &op->last_sframe)
+- kfree(op->last_frames);
+- kfree(op);
+- return err;
+- }
++ if (err < 0)
++ goto free_op;
+
+- bcm_rx_setup_rtr_check(msg_head, op, op->frames);
++ err = bcm_rx_setup_rtr_check(msg_head, op, op->frames);
++ if (err < 0)
++ goto free_op;
+ }
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+@@ -1456,6 +1471,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ }
+
+ return msg_head->nframes * op->cfsiz + MHSIZ;
++
++free_op:
++ if (op->frames != &op->sframe)
++ kfree(op->frames);
++ if (op->last_frames != &op->last_sframe)
++ kfree(op->last_frames);
++ kfree(op);
++ return err;
+ }
+
+ /*
+--
+2.53.0
+
raw-use-more-conventional-iterators.patch
net-ipv6-fix-dif-and-sdif-mismatch-in-raw6_icmp_erro.patch
bpf-sockmap-fix-cork-use-after-free-in-tcp_bpf_sendm.patch
+can-bcm-defer-rx_op-deallocation-to-workqueue-to-fix.patch
+can-bcm-add-locking-when-updating-filter-and-timer-v.patch
+can-bcm-fix-can-frame-rx-tx-statistics.patch
+can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
+can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
+can-bcm-add-missing-device-refcount-for-can-filter-r.patch
+can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
+can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
+can-bcm-track-a-single-source-interface-for-anydev-t.patch