]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 26 Jul 2026 12:04:50 +0000 (08:04 -0400)
committerSasha Levin <sashal@kernel.org>
Sun, 26 Jul 2026 12:04:50 +0000 (08:04 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
82 files changed:
queue-5.10/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch [new file with mode: 0644]
queue-5.10/can-isotp-serialize-tx-state-transitions-under-so-rx.patch [new file with mode: 0644]
queue-5.10/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-5.10/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch [new file with mode: 0644]
queue-5.15/can-isotp-serialize-tx-state-transitions-under-so-rx.patch [new file with mode: 0644]
queue-5.15/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch [new file with mode: 0644]
queue-5.15/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-5.15/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/can-bcm-add-locking-when-updating-filter-and-timer-v.patch [new file with mode: 0644]
queue-6.1/can-bcm-add-missing-device-refcount-for-can-filter-r.patch [new file with mode: 0644]
queue-6.1/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch [new file with mode: 0644]
queue-6.1/can-bcm-fix-can-frame-rx-tx-statistics.patch [new file with mode: 0644]
queue-6.1/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch [new file with mode: 0644]
queue-6.1/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch [new file with mode: 0644]
queue-6.1/can-bcm-track-a-single-source-interface-for-anydev-t.patch [new file with mode: 0644]
queue-6.1/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch [new file with mode: 0644]
queue-6.1/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch [new file with mode: 0644]
queue-6.1/can-isotp-serialize-tx-state-transitions-under-so-rx.patch [new file with mode: 0644]
queue-6.1/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch [new file with mode: 0644]
queue-6.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch [new file with mode: 0644]
queue-6.1/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-6.1/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-6.1/series
queue-6.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch [new file with mode: 0644]
queue-6.12/can-bcm-add-locking-when-updating-filter-and-timer-v.patch [new file with mode: 0644]
queue-6.12/can-bcm-add-missing-device-refcount-for-can-filter-r.patch [new file with mode: 0644]
queue-6.12/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch [new file with mode: 0644]
queue-6.12/can-bcm-fix-can-frame-rx-tx-statistics.patch [new file with mode: 0644]
queue-6.12/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch [new file with mode: 0644]
queue-6.12/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch [new file with mode: 0644]
queue-6.12/can-bcm-track-a-single-source-interface-for-anydev-t.patch [new file with mode: 0644]
queue-6.12/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch [new file with mode: 0644]
queue-6.12/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch [new file with mode: 0644]
queue-6.12/can-isotp-serialize-tx-state-transitions-under-so-rx.patch [new file with mode: 0644]
queue-6.12/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch [new file with mode: 0644]
queue-6.12/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch [new file with mode: 0644]
queue-6.12/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch [new file with mode: 0644]
queue-6.12/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-6.12/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-6.12/revert-arm64-dts-ti-k3-am62a7-sk-add-bootph-all-tag-.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch [new file with mode: 0644]
queue-6.12/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch [new file with mode: 0644]
queue-6.18/can-bcm-add-locking-when-updating-filter-and-timer-v.patch [new file with mode: 0644]
queue-6.18/can-bcm-add-missing-device-refcount-for-can-filter-r.patch [new file with mode: 0644]
queue-6.18/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch [new file with mode: 0644]
queue-6.18/can-bcm-fix-can-frame-rx-tx-statistics.patch [new file with mode: 0644]
queue-6.18/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch [new file with mode: 0644]
queue-6.18/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch [new file with mode: 0644]
queue-6.18/can-bcm-track-a-single-source-interface-for-anydev-t.patch [new file with mode: 0644]
queue-6.18/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch [new file with mode: 0644]
queue-6.18/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch [new file with mode: 0644]
queue-6.18/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch [new file with mode: 0644]
queue-6.18/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch [new file with mode: 0644]
queue-6.18/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-6.18/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch [new file with mode: 0644]
queue-6.6/accel-ivpu-reject-firmware-log-with-size-smaller-tha.patch [new file with mode: 0644]
queue-6.6/can-bcm-add-locking-when-updating-filter-and-timer-v.patch [new file with mode: 0644]
queue-6.6/can-bcm-add-missing-device-refcount-for-can-filter-r.patch [new file with mode: 0644]
queue-6.6/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch [new file with mode: 0644]
queue-6.6/can-bcm-fix-can-frame-rx-tx-statistics.patch [new file with mode: 0644]
queue-6.6/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch [new file with mode: 0644]
queue-6.6/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch [new file with mode: 0644]
queue-6.6/can-bcm-track-a-single-source-interface-for-anydev-t.patch [new file with mode: 0644]
queue-6.6/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch [new file with mode: 0644]
queue-6.6/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch [new file with mode: 0644]
queue-6.6/can-isotp-serialize-tx-state-transitions-under-so-rx.patch [new file with mode: 0644]
queue-6.6/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch [new file with mode: 0644]
queue-6.6/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch [new file with mode: 0644]
queue-6.6/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch [new file with mode: 0644]
queue-6.6/input-ims-pcu-fix-logic-error-in-packet-reset.patch [new file with mode: 0644]
queue-6.6/series
queue-6.6/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch [new file with mode: 0644]
queue-7.1/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch [new file with mode: 0644]
queue-7.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch [new file with mode: 0644]
queue-7.1/series
queue-7.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch [new file with mode: 0644]

diff --git a/queue-5.10/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch b/queue-5.10/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
new file mode 100644 (file)
index 0000000..6b7b02d
--- /dev/null
@@ -0,0 +1,238 @@
+From 0e74c56e42514516ffe21b4971c66a9395662a82 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:27:18 +0200
+Subject: can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream.
+
+isotp_release() looked up the bound network device via dev_get_by_index()
+using the stored ifindex. During device unregistration the device is
+unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier
+chain runs, so a concurrent isotp_release() could find no device, skip
+can_rx_unregister() entirely, and still proceed to free the socket.
+Since isotp_release() had already removed itself from the isotp
+notifier list at that point, isotp_notify() would never get a chance to
+clean up either, leaving a stale CAN filter that keeps pointing at the
+freed socket.
+
+Fix this the same way raw.c already does: hold a tracked reference to
+the bound net_device in the socket (so->dev/so->dev_tracker) from
+bind() onward instead of re-resolving it from the ifindex, and
+serialize bind()/release() with rtnl_lock() so that so->dev is always
+consistent with what the NETDEV_UNREGISTER notifier sees. so->dev
+stays valid regardless of ifindex-hash unlisting, and is only ever
+cleared by whichever of isotp_release()/isotp_notify() gets there
+first, so the filter is always removed exactly once.
+
+isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state
+isn't ISOTP_IDLE yet, so a timer left running by a prior
+NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks
+share the same lock_sock() section, so there is no window in which a
+concurrent isotp_notify() clearing so->bound could be missed.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@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/isotp.c | 88 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 59 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index c914b8a09155b3..169fb17d0f0e16 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -137,6 +137,7 @@ struct isotp_sock {
+       struct sock sk;
+       int bound;
+       int ifindex;
++      struct net_device *dev;
+       canid_t txid;
+       canid_t rxid;
+       ktime_t tx_gap;
+@@ -939,6 +940,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       goto err_event_drop;
+       }
++      /* so->bound is only checked once above - a wakeup may have
++       * unbound/rebound the socket meanwhile, so re-validate it
++       */
++      if (!so->bound) {
++              err = -EADDRNOTAVAIL;
++              goto err_out_drop;
++      }
++
+       if (!size || size > MAX_MSG_LENGTH) {
+               err = -EINVAL;
+               goto err_out_drop;
+@@ -1166,28 +1175,30 @@ static int isotp_release(struct socket *sock)
+       list_del(&so->notifier);
+       spin_unlock(&isotp_notifier_lock);
++      rtnl_lock();
+       lock_sock(sk);
+-      /* remove current filters & unregister */
+-      if (so->bound) {
+-              if (so->ifindex) {
+-                      struct net_device *dev;
+-
+-                      dev = dev_get_by_index(net, so->ifindex);
+-                      if (dev) {
+-                              if (isotp_register_rxid(so))
+-                                      can_rx_unregister(net, dev, so->rxid,
+-                                                        SINGLE_MASK(so->rxid),
+-                                                        isotp_rcv, sk);
+-
+-                              can_rx_unregister(net, dev, so->txid,
+-                                                SINGLE_MASK(so->txid),
+-                                                isotp_rcv_echo, sk);
+-                              dev_put(dev);
+-                      }
+-              }
++      /* remove current filters & unregister
++       * tracked reference so->dev is taken at bind() time with rtnl_lock
++       */
++      if (so->bound && so->dev) {
++              if (isotp_register_rxid(so))
++                      can_rx_unregister(net, so->dev, so->rxid,
++                                        SINGLE_MASK(so->rxid),
++                                        isotp_rcv, sk);
++
++              can_rx_unregister(net, so->dev, so->txid,
++                                SINGLE_MASK(so->txid),
++                                isotp_rcv_echo, sk);
++              dev_put(so->dev);
+       }
++      so->ifindex = 0;
++      so->bound = 0;
++      so->dev = NULL;
++
++      rtnl_unlock();
++
+       /* Always wait for a grace period before touching the timers below.
+        * A concurrent NETDEV_UNREGISTER may have already unregistered our
+        * filters and cleared so->bound in isotp_notify() without waiting
+@@ -1200,9 +1211,6 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txtimer);
+       hrtimer_cancel(&so->rxtimer);
+-      so->ifindex = 0;
+-      so->bound = 0;
+-
+       sock_orphan(sk);
+       sock->sk = NULL;
+@@ -1256,6 +1264,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       if (!addr->can_ifindex)
+               return -ENODEV;
++      rtnl_lock();
+       lock_sock(sk);
+       if (so->bound) {
+@@ -1263,6 +1272,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
++      /* A transmission or reception that outlived a previous binding
++       * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
++       * and RX watchdog timers bound how long this takes. Checked together
++       * with so->bound in the same lock_sock() section above, so there is
++       * no window in which a concurrent isotp_notify() could be missed.
++       */
++      if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
++              err = -EAGAIN;
++              goto out;
++      }
++
+       /* ensure different CAN IDs when the rx_id is to be registered */
+       if (isotp_register_rxid(so) && rx_id == tx_id) {
+               err = -EADDRNOTAVAIL;
+@@ -1275,14 +1295,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
+       if (dev->type != ARPHRD_CAN) {
+-              dev_put(dev);
+               err = -ENODEV;
+-              goto out;
++              goto out_put_dev;
+       }
+-      if (dev->mtu < so->ll.mtu) {
+-              dev_put(dev);
++      if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+               err = -EINVAL;
+-              goto out;
++              goto out_put_dev;
+       }
+       if (!(dev->flags & IFF_UP))
+               notify_enetdown = 1;
+@@ -1300,16 +1318,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
+                       isotp_rcv_echo, sk, "isotpe", sk);
+-      dev_put(dev);
+-
+       /* switch to new settings */
+       so->ifindex = ifindex;
+       so->rxid = rx_id;
+       so->txid = tx_id;
+       so->bound = 1;
++      /* bind() ok -> hold a reference for so->dev so that isotp_release()
++       * can safely reach the device later, even if a concurrent
++       * NETDEV_UNREGISTER has already unlisted it by ifindex.
++       */
++      so->dev = dev;
++      dev_hold(so->dev);
++
++out_put_dev:
++      /* remove potential reference from dev_get_by_index() */
++      dev_put(dev);
+ out:
+       release_sock(sk);
++      rtnl_unlock();
+       if (notify_enetdown) {
+               sk->sk_err = ENETDOWN;
+@@ -1512,7 +1539,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return;
+-      if (so->ifindex != dev->ifindex)
++      if (so->dev != dev)
+               return;
+       switch (msg) {
+@@ -1528,10 +1555,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+                       can_rx_unregister(dev_net(dev), dev, so->txid,
+                                         SINGLE_MASK(so->txid),
+                                         isotp_rcv_echo, sk);
++                      dev_put(so->dev);
+               }
+               so->ifindex = 0;
+               so->bound  = 0;
++              so->dev = NULL;
+               release_sock(sk);
+               sk->sk_err = ENODEV;
+@@ -1576,6 +1605,7 @@ static int isotp_init(struct sock *sk)
+       so->ifindex = 0;
+       so->bound = 0;
++      so->dev = NULL;
+       so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+       so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+-- 
+2.53.0
+
diff --git a/queue-5.10/can-isotp-serialize-tx-state-transitions-under-so-rx.patch b/queue-5.10/can-isotp-serialize-tx-state-transitions-under-so-rx.patch
new file mode 100644 (file)
index 0000000..376668b
--- /dev/null
@@ -0,0 +1,426 @@
+From 95444e5a3d79567ba3e4dc19ec534072c8628e67 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:27:19 +0200
+Subject: can: isotp: serialize TX state transitions under so->rx_lock
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream.
+
+The TX state machine (so->tx.state) is driven from three contexts:
+sendmsg() claiming and progressing a transfer, the RX path consuming
+Flow Control/echo frames, and two hrtimers timing out a stalled
+transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with
+hrtimer_cancel() calls made under so->rx_lock elsewhere left windows
+where a frame or timer callback could act on a state that had already
+moved on, corrupting an unrelated transfer.
+
+so->rx_lock now covers the full lifecycle of a TX claim: sendmsg()
+takes it to check so->tx.state is ISOTP_IDLE, switch it to
+ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's
+timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf()
+already run under this lock via isotp_rcv(), and isotp_rcv_echo() now
+takes it itself, so none of them can ever observe a transfer mid-claim.
+This also means a transfer can no longer be handed to sendmsg()'s
+cleanup paths (signal or send error) while another thread is
+concurrently claiming or finishing it, so those paths can cancel
+timers and reset the state unconditionally.
+
+isotp_release() claims the socket the same way, so a racing sendmsg()
+sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending.
+
+Only the hrtimer callbacks stay outside so->rx_lock, since they run
+under so->rx_lock's cancellation elsewhere and taking it themselves
+would deadlock. so->tx_gen lets them recognize whether the transfer
+they timed out is still the one currently active, so they don't
+report an error against a transfer that has since completed or been
+superseded.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@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/isotp.c | 192 ++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 154 insertions(+), 38 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 169fb17d0f0e16..d493b66ae8d29f 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -142,7 +142,7 @@ struct isotp_sock {
+       canid_t rxid;
+       ktime_t tx_gap;
+       ktime_t lastrxcf_tstamp;
+-      struct hrtimer rxtimer, txtimer, txfrtimer;
++      struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
+       struct can_isotp_options opt;
+       struct can_isotp_fc_options rxfc, txfc;
+       struct can_isotp_ll_options ll;
+@@ -150,6 +150,7 @@ struct isotp_sock {
+       u32 force_tx_stmin;
+       u32 force_rx_stmin;
+       u32 cfecho; /* consecutive frame echo tag */
++      u32 tx_gen; /* generation, bumped per new tx transfer */
+       struct tpcon rx, tx;
+       struct list_head notifier;
+       wait_queue_head_t wait;
+@@ -356,6 +357,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+       hrtimer_cancel(&so->txtimer);
++      /* isotp_tx_timeout() may have given up on this job while
++       * hrtimer_cancel() above waited for it to finish; so->rx_lock
++       * (held by our caller isotp_rcv()) rules out a concurrent claim,
++       * so a plain recheck is enough here.
++       */
++      if (so->tx.state != ISOTP_WAIT_FC &&
++          so->tx.state != ISOTP_WAIT_FIRST_FC)
++              return 1;
++
+       if ((cf->len < ae + FC_CONTENT_SZ) ||
+           ((so->opt.flags & ISOTP_CHECK_PADDING) &&
+            check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
+@@ -401,7 +411,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+               so->tx.bs = 0;
+               so->tx.state = ISOTP_SENDING;
+               /* send CF frame and enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+               break;
+@@ -544,6 +554,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
+       hrtimer_cancel(&so->rxtimer);
++      /* isotp_rx_timer_handler() may have raced us for so->rx.state
++       * while hrtimer_cancel() above waited for it to finish, already
++       * reporting ETIMEDOUT and resetting the reception; don't process
++       * this CF into a reassembly that has already been given up on.
++       */
++      if (so->rx.state != ISOTP_WAIT_DATA)
++              return 1;
++
+       /* CFs are never longer than the FF */
+       if (cf->len > so->rx.ll_dl)
+               return 1;
+@@ -833,20 +851,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       /* only handle my own local echo CF/SF skb's (no FF!) */
+-      if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
++      if (skb->sk != sk)
+               return;
++      /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
++       * (no isotp_rcv() caller here), so take it ourselves
++       */
++      spin_lock(&so->rx_lock);
++
++      /* so->cfecho may since belong to a new transfer; recheck under lock */
++      if (so->cfecho != *(u32 *)cf->data)
++              goto out_unlock;
++
+       /* cancel local echo timeout */
+-      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       /* local echo skb with consecutive frame has been consumed */
+       so->cfecho = 0;
++      /* claiming a transfer also takes so->rx_lock, so a plain recheck
++       * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
++       * a new claim while we're still in here
++       */
++      if (so->tx.state != ISOTP_SENDING)
++              goto out_unlock;
++
+       if (so->tx.idx >= so->tx.len) {
+               /* we are done */
+               so->tx.state = ISOTP_IDLE;
+               wake_up_interruptible(&so->wait);
+-              return;
++              goto out_unlock;
+       }
+       if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+@@ -854,53 +888,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+               so->tx.state = ISOTP_WAIT_FC;
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+-              return;
++              goto out_unlock;
+       }
+       /* no gap between data frames needed => use burst mode */
+       if (!so->tx_gap) {
+               /* enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+-              return;
++              goto out_unlock;
+       }
+       /* start timer to send next consecutive frame with correct delay */
+       hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
++
++out_unlock:
++      spin_unlock(&so->rx_lock);
+ }
+-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
++ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
++ * a new transfer with an error from the one that just timed out.
++ */
++static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
+ {
+-      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+-                                           txtimer);
+       struct sock *sk = &so->sk;
++      u32 gen = READ_ONCE(so->tx_gen);
++      u32 old_state = READ_ONCE(so->tx.state);
+       /* don't handle timeouts in IDLE or SHUTDOWN state */
+-      if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
++      if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
++              return HRTIMER_NORESTART;
++
++      /* only claim the timeout if the state is still unchanged */
++      if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
+               return HRTIMER_NORESTART;
+       /* we did not get any flow control or echo frame in time */
+-      /* report 'communication error on send' */
+-      sk->sk_err = ECOMM;
+-      if (!sock_flag(sk, SOCK_DEAD))
+-              sk->sk_error_report(sk);
++      if (READ_ONCE(so->tx_gen) == gen) {
++              /* report 'communication error on send' */
++              sk->sk_err = ECOMM;
++              if (!sock_flag(sk, SOCK_DEAD))
++                      sk->sk_error_report(sk);
++      }
+-      /* reset tx state */
+-      so->tx.state = ISOTP_IDLE;
+       wake_up_interruptible(&so->wait);
+       return HRTIMER_NORESTART;
+ }
++/* so->txtimer: fires when a Flow Control frame does not arrive in time */
++static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           txtimer);
++
++      return isotp_tx_timeout(so);
++}
++
++/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
++static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           echotimer);
++
++      return isotp_tx_timeout(so);
++}
++
+ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+ {
+       struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+                                            txfrtimer);
+       /* start echo timeout handling and cover below protocol error */
+-      hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++      hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
+       /* cfecho should be consumed by isotp_rcv_echo() here */
+@@ -920,13 +984,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+       int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
++      struct hrtimer *tx_hrt = &so->echotimer;
++      u32 new_state = ISOTP_SENDING;
+       int off;
+       int err;
+       if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+               return -EADDRNOTAVAIL;
+-      while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
++      /* claim the socket under so->rx_lock: this serializes the claim
++       * with the RX path and with sendmsg()'s own error paths below, so
++       * none of them can ever see a transfer mid-claim
++       */
++      for (;;) {
++              spin_lock_bh(&so->rx_lock);
++              if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
++                      break;
++              spin_unlock_bh(&so->rx_lock);
++
+               /* we do not support multiple buffers - for now */
+               if (msg->msg_flags & MSG_DONTWAIT)
+                       return -EAGAIN;
+@@ -935,11 +1010,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       return -EADDRNOTAVAIL;
+               /* wait for complete transmission of current pdu */
+-              err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
++              err = wait_event_interruptible(so->wait,
++                                             so->tx.state == ISOTP_IDLE);
+               if (err)
+-                      goto err_event_drop;
++                      return err;
+       }
++      /* new transfer: bump so->tx_gen and drain the old one's timers,
++       * still under the so->rx_lock we just claimed the socket with
++       */
++      WRITE_ONCE(so->tx.state, ISOTP_SENDING);
++      WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
++      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
++      hrtimer_cancel(&so->txfrtimer);
++      so->cfecho = 0;
++      spin_unlock_bh(&so->rx_lock);
++
+       /* so->bound is only checked once above - a wakeup may have
+        * unbound/rebound the socket meanwhile, so re-validate it
+        */
+@@ -1040,18 +1127,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       so->cfecho = *(u32 *)cf->data;
+               } else {
+                       /* standard flow control check */
+-                      so->tx.state = ISOTP_WAIT_FIRST_FC;
++                      new_state = ISOTP_WAIT_FIRST_FC;
+                       /* start timeout for FC */
+                       hrtimer_sec = ISOTP_FC_TIMEOUT;
++                      tx_hrt = &so->txtimer;
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
+       }
+-      hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
++      spin_lock_bh(&so->rx_lock);
++      if (so->tx.state == ISOTP_SHUTDOWN) {
++              /* isotp_release() has since taken over and already drained
++               * our timers - don't send into a socket that's going away
++               */
++              spin_unlock_bh(&so->rx_lock);
++              kfree_skb(skb);
++              dev_put(dev);
++              wake_up_interruptible(&so->wait);
++              return -EADDRNOTAVAIL;
++      }
++      /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
++      so->tx.state = new_state;
++      hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
++      spin_unlock_bh(&so->rx_lock);
+       /* send the first or only CAN frame */
+       cf->flags = so->ll.tx_flags;
+@@ -1064,13 +1166,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+               pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
+                              __func__, ERR_PTR(err));
++              spin_lock_bh(&so->rx_lock);
+               /* no transmission -> no timeout monitoring */
+-              hrtimer_cancel(&so->txtimer);
+-
+-              /* reset consecutive frame echo tag */
+-              so->cfecho = 0;
+-
+-              goto err_out_drop;
++              hrtimer_cancel(tx_hrt);
++              goto err_out_drop_locked;
+       }
+       if (wait_tx_done) {
+@@ -1086,14 +1185,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       return size;
++err_out_drop:
++      /* claimed but nothing sent yet - no timer to cancel */
++      spin_lock_bh(&so->rx_lock);
++      goto err_out_drop_locked;
+ err_event_drop:
+-      /* got signal: force tx state machine to be idle */
+-      so->tx.state = ISOTP_IDLE;
++      /* interrupted waiting on our own transfer - drain its timers */
++      spin_lock_bh(&so->rx_lock);
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
+-err_out_drop:
+-      /* drop this PDU and unlock a potential wait queue */
++      hrtimer_cancel(&so->echotimer);
++err_out_drop_locked:
++      /* release the claim; so->rx_lock still held from above */
++      so->cfecho = 0;
+       so->tx.state = ISOTP_IDLE;
++      spin_unlock_bh(&so->rx_lock);
+       wake_up_interruptible(&so->wait);
+       return err;
+@@ -1157,13 +1263,20 @@ static int isotp_release(struct socket *sock)
+       so = isotp_sk(sk);
+       net = sock_net(sk);
+-      /* wait for complete transmission of current pdu */
+-      while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
+-             cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
++      /* best-effort: wait for a running pdu to finish, but don't block on
++       * it forever - give up after the first signal
++       */
++      while (so->tx.state != ISOTP_IDLE &&
++             wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+               ;
+-      /* force state machines to be idle also when a signal occurred */
++      /* claim the socket under so->rx_lock like sendmsg() does, so its
++       * claim can't race the forced ISOTP_SHUTDOWN below; force it
++       * unconditionally, even when a signal cut the wait above short
++       */
++      spin_lock_bh(&so->rx_lock);
+       so->tx.state = ISOTP_SHUTDOWN;
++      spin_unlock_bh(&so->rx_lock);
+       so->rx.state = ISOTP_IDLE;
+       spin_lock(&isotp_notifier_lock);
+@@ -1209,6 +1322,7 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       hrtimer_cancel(&so->rxtimer);
+       sock_orphan(sk);
+@@ -1631,6 +1745,8 @@ static int isotp_init(struct sock *sk)
+       so->rxtimer.function = isotp_rx_timer_handler;
+       hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txtimer.function = isotp_tx_timer_handler;
++      hrtimer_init(&so->echotimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
++      so->echotimer.function = isotp_echo_timer_handler;
+       hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txfrtimer.function = isotp_txfr_timer_handler;
+-- 
+2.53.0
+
diff --git a/queue-5.10/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-5.10/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..ae3586d
--- /dev/null
@@ -0,0 +1,114 @@
+From e77c83e15c194e2f9168fe2002d8270273b0b6ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 55d4fffda41bf2..71d96e7d842dab 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-5.10/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-5.10/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..597fc5c
--- /dev/null
@@ -0,0 +1,42 @@
+From 62fd485d1b40603dae67943051702d875911ea97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 71d96e7d842dab..0214c3bb2b096d 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
index ef2f5b1b1f2eaa72ef68c3bc057c6a8eba937886..c4a8a5edea85fa59b7a9bc6a27c85b5eab688766 100644 (file)
@@ -3,3 +3,7 @@ cpu-hotplug-bound-hotplug-states-sysfs-output.patch
 macsec-don-t-read-an-unset-mac-header-in-macsec_encr.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.patch
+can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
+can-isotp-serialize-tx-state-transitions-under-so-rx.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
diff --git a/queue-5.15/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch b/queue-5.15/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
new file mode 100644 (file)
index 0000000..3b929c5
--- /dev/null
@@ -0,0 +1,238 @@
+From afb9618476c4dcb9b28dc35aea2e2283486c7223 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:32:49 +0200
+Subject: can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream.
+
+isotp_release() looked up the bound network device via dev_get_by_index()
+using the stored ifindex. During device unregistration the device is
+unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier
+chain runs, so a concurrent isotp_release() could find no device, skip
+can_rx_unregister() entirely, and still proceed to free the socket.
+Since isotp_release() had already removed itself from the isotp
+notifier list at that point, isotp_notify() would never get a chance to
+clean up either, leaving a stale CAN filter that keeps pointing at the
+freed socket.
+
+Fix this the same way raw.c already does: hold a tracked reference to
+the bound net_device in the socket (so->dev/so->dev_tracker) from
+bind() onward instead of re-resolving it from the ifindex, and
+serialize bind()/release() with rtnl_lock() so that so->dev is always
+consistent with what the NETDEV_UNREGISTER notifier sees. so->dev
+stays valid regardless of ifindex-hash unlisting, and is only ever
+cleared by whichever of isotp_release()/isotp_notify() gets there
+first, so the filter is always removed exactly once.
+
+isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state
+isn't ISOTP_IDLE yet, so a timer left running by a prior
+NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks
+share the same lock_sock() section, so there is no window in which a
+concurrent isotp_notify() clearing so->bound could be missed.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@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/isotp.c | 88 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 59 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 813134f549f6c1..ee63f0de7ff543 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -137,6 +137,7 @@ struct isotp_sock {
+       struct sock sk;
+       int bound;
+       int ifindex;
++      struct net_device *dev;
+       canid_t txid;
+       canid_t rxid;
+       ktime_t tx_gap;
+@@ -939,6 +940,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       goto err_event_drop;
+       }
++      /* so->bound is only checked once above - a wakeup may have
++       * unbound/rebound the socket meanwhile, so re-validate it
++       */
++      if (!so->bound) {
++              err = -EADDRNOTAVAIL;
++              goto err_out_drop;
++      }
++
+       if (!size || size > MAX_MSG_LENGTH) {
+               err = -EINVAL;
+               goto err_out_drop;
+@@ -1166,28 +1175,30 @@ static int isotp_release(struct socket *sock)
+       list_del(&so->notifier);
+       spin_unlock(&isotp_notifier_lock);
++      rtnl_lock();
+       lock_sock(sk);
+-      /* remove current filters & unregister */
+-      if (so->bound) {
+-              if (so->ifindex) {
+-                      struct net_device *dev;
+-
+-                      dev = dev_get_by_index(net, so->ifindex);
+-                      if (dev) {
+-                              if (isotp_register_rxid(so))
+-                                      can_rx_unregister(net, dev, so->rxid,
+-                                                        SINGLE_MASK(so->rxid),
+-                                                        isotp_rcv, sk);
+-
+-                              can_rx_unregister(net, dev, so->txid,
+-                                                SINGLE_MASK(so->txid),
+-                                                isotp_rcv_echo, sk);
+-                              dev_put(dev);
+-                      }
+-              }
++      /* remove current filters & unregister
++       * tracked reference so->dev is taken at bind() time with rtnl_lock
++       */
++      if (so->bound && so->dev) {
++              if (isotp_register_rxid(so))
++                      can_rx_unregister(net, so->dev, so->rxid,
++                                        SINGLE_MASK(so->rxid),
++                                        isotp_rcv, sk);
++
++              can_rx_unregister(net, so->dev, so->txid,
++                                SINGLE_MASK(so->txid),
++                                isotp_rcv_echo, sk);
++              dev_put(so->dev);
+       }
++      so->ifindex = 0;
++      so->bound = 0;
++      so->dev = NULL;
++
++      rtnl_unlock();
++
+       /* Always wait for a grace period before touching the timers below.
+        * A concurrent NETDEV_UNREGISTER may have already unregistered our
+        * filters and cleared so->bound in isotp_notify() without waiting
+@@ -1200,9 +1211,6 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txtimer);
+       hrtimer_cancel(&so->rxtimer);
+-      so->ifindex = 0;
+-      so->bound = 0;
+-
+       sock_orphan(sk);
+       sock->sk = NULL;
+@@ -1256,6 +1264,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       if (!addr->can_ifindex)
+               return -ENODEV;
++      rtnl_lock();
+       lock_sock(sk);
+       if (so->bound) {
+@@ -1263,6 +1272,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
++      /* A transmission or reception that outlived a previous binding
++       * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
++       * and RX watchdog timers bound how long this takes. Checked together
++       * with so->bound in the same lock_sock() section above, so there is
++       * no window in which a concurrent isotp_notify() could be missed.
++       */
++      if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
++              err = -EAGAIN;
++              goto out;
++      }
++
+       /* ensure different CAN IDs when the rx_id is to be registered */
+       if (isotp_register_rxid(so) && rx_id == tx_id) {
+               err = -EADDRNOTAVAIL;
+@@ -1275,14 +1295,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
+       if (dev->type != ARPHRD_CAN) {
+-              dev_put(dev);
+               err = -ENODEV;
+-              goto out;
++              goto out_put_dev;
+       }
+-      if (dev->mtu < so->ll.mtu) {
+-              dev_put(dev);
++      if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+               err = -EINVAL;
+-              goto out;
++              goto out_put_dev;
+       }
+       if (!(dev->flags & IFF_UP))
+               notify_enetdown = 1;
+@@ -1300,16 +1318,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
+                       isotp_rcv_echo, sk, "isotpe", sk);
+-      dev_put(dev);
+-
+       /* switch to new settings */
+       so->ifindex = ifindex;
+       so->rxid = rx_id;
+       so->txid = tx_id;
+       so->bound = 1;
++      /* bind() ok -> hold a reference for so->dev so that isotp_release()
++       * can safely reach the device later, even if a concurrent
++       * NETDEV_UNREGISTER has already unlisted it by ifindex.
++       */
++      so->dev = dev;
++      dev_hold(so->dev);
++
++out_put_dev:
++      /* remove potential reference from dev_get_by_index() */
++      dev_put(dev);
+ out:
+       release_sock(sk);
++      rtnl_unlock();
+       if (notify_enetdown) {
+               sk->sk_err = ENETDOWN;
+@@ -1512,7 +1539,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return;
+-      if (so->ifindex != dev->ifindex)
++      if (so->dev != dev)
+               return;
+       switch (msg) {
+@@ -1528,10 +1555,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+                       can_rx_unregister(dev_net(dev), dev, so->txid,
+                                         SINGLE_MASK(so->txid),
+                                         isotp_rcv_echo, sk);
++                      dev_put(so->dev);
+               }
+               so->ifindex = 0;
+               so->bound  = 0;
++              so->dev = NULL;
+               release_sock(sk);
+               sk->sk_err = ENODEV;
+@@ -1576,6 +1605,7 @@ static int isotp_init(struct sock *sk)
+       so->ifindex = 0;
+       so->bound = 0;
++      so->dev = NULL;
+       so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+       so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+-- 
+2.53.0
+
diff --git a/queue-5.15/can-isotp-serialize-tx-state-transitions-under-so-rx.patch b/queue-5.15/can-isotp-serialize-tx-state-transitions-under-so-rx.patch
new file mode 100644 (file)
index 0000000..9050fb5
--- /dev/null
@@ -0,0 +1,426 @@
+From 6ca343a2e610dfe9037acae9f404e3f94c9d3985 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:32:50 +0200
+Subject: can: isotp: serialize TX state transitions under so->rx_lock
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream.
+
+The TX state machine (so->tx.state) is driven from three contexts:
+sendmsg() claiming and progressing a transfer, the RX path consuming
+Flow Control/echo frames, and two hrtimers timing out a stalled
+transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with
+hrtimer_cancel() calls made under so->rx_lock elsewhere left windows
+where a frame or timer callback could act on a state that had already
+moved on, corrupting an unrelated transfer.
+
+so->rx_lock now covers the full lifecycle of a TX claim: sendmsg()
+takes it to check so->tx.state is ISOTP_IDLE, switch it to
+ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's
+timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf()
+already run under this lock via isotp_rcv(), and isotp_rcv_echo() now
+takes it itself, so none of them can ever observe a transfer mid-claim.
+This also means a transfer can no longer be handed to sendmsg()'s
+cleanup paths (signal or send error) while another thread is
+concurrently claiming or finishing it, so those paths can cancel
+timers and reset the state unconditionally.
+
+isotp_release() claims the socket the same way, so a racing sendmsg()
+sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending.
+
+Only the hrtimer callbacks stay outside so->rx_lock, since they run
+under so->rx_lock's cancellation elsewhere and taking it themselves
+would deadlock. so->tx_gen lets them recognize whether the transfer
+they timed out is still the one currently active, so they don't
+report an error against a transfer that has since completed or been
+superseded.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@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/isotp.c | 192 ++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 154 insertions(+), 38 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index ee63f0de7ff543..de6992580c38cb 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -142,7 +142,7 @@ struct isotp_sock {
+       canid_t rxid;
+       ktime_t tx_gap;
+       ktime_t lastrxcf_tstamp;
+-      struct hrtimer rxtimer, txtimer, txfrtimer;
++      struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
+       struct can_isotp_options opt;
+       struct can_isotp_fc_options rxfc, txfc;
+       struct can_isotp_ll_options ll;
+@@ -150,6 +150,7 @@ struct isotp_sock {
+       u32 force_tx_stmin;
+       u32 force_rx_stmin;
+       u32 cfecho; /* consecutive frame echo tag */
++      u32 tx_gen; /* generation, bumped per new tx transfer */
+       struct tpcon rx, tx;
+       struct list_head notifier;
+       wait_queue_head_t wait;
+@@ -356,6 +357,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+       hrtimer_cancel(&so->txtimer);
++      /* isotp_tx_timeout() may have given up on this job while
++       * hrtimer_cancel() above waited for it to finish; so->rx_lock
++       * (held by our caller isotp_rcv()) rules out a concurrent claim,
++       * so a plain recheck is enough here.
++       */
++      if (so->tx.state != ISOTP_WAIT_FC &&
++          so->tx.state != ISOTP_WAIT_FIRST_FC)
++              return 1;
++
+       if ((cf->len < ae + FC_CONTENT_SZ) ||
+           ((so->opt.flags & ISOTP_CHECK_PADDING) &&
+            check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
+@@ -401,7 +411,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+               so->tx.bs = 0;
+               so->tx.state = ISOTP_SENDING;
+               /* send CF frame and enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+               break;
+@@ -544,6 +554,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
+       hrtimer_cancel(&so->rxtimer);
++      /* isotp_rx_timer_handler() may have raced us for so->rx.state
++       * while hrtimer_cancel() above waited for it to finish, already
++       * reporting ETIMEDOUT and resetting the reception; don't process
++       * this CF into a reassembly that has already been given up on.
++       */
++      if (so->rx.state != ISOTP_WAIT_DATA)
++              return 1;
++
+       /* CFs are never longer than the FF */
+       if (cf->len > so->rx.ll_dl)
+               return 1;
+@@ -833,20 +851,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       /* only handle my own local echo CF/SF skb's (no FF!) */
+-      if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
++      if (skb->sk != sk)
+               return;
++      /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
++       * (no isotp_rcv() caller here), so take it ourselves
++       */
++      spin_lock(&so->rx_lock);
++
++      /* so->cfecho may since belong to a new transfer; recheck under lock */
++      if (so->cfecho != *(u32 *)cf->data)
++              goto out_unlock;
++
+       /* cancel local echo timeout */
+-      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       /* local echo skb with consecutive frame has been consumed */
+       so->cfecho = 0;
++      /* claiming a transfer also takes so->rx_lock, so a plain recheck
++       * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
++       * a new claim while we're still in here
++       */
++      if (so->tx.state != ISOTP_SENDING)
++              goto out_unlock;
++
+       if (so->tx.idx >= so->tx.len) {
+               /* we are done */
+               so->tx.state = ISOTP_IDLE;
+               wake_up_interruptible(&so->wait);
+-              return;
++              goto out_unlock;
+       }
+       if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+@@ -854,53 +888,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+               so->tx.state = ISOTP_WAIT_FC;
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+-              return;
++              goto out_unlock;
+       }
+       /* no gap between data frames needed => use burst mode */
+       if (!so->tx_gap) {
+               /* enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+-              return;
++              goto out_unlock;
+       }
+       /* start timer to send next consecutive frame with correct delay */
+       hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
++
++out_unlock:
++      spin_unlock(&so->rx_lock);
+ }
+-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
++ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
++ * a new transfer with an error from the one that just timed out.
++ */
++static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
+ {
+-      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+-                                           txtimer);
+       struct sock *sk = &so->sk;
++      u32 gen = READ_ONCE(so->tx_gen);
++      u32 old_state = READ_ONCE(so->tx.state);
+       /* don't handle timeouts in IDLE or SHUTDOWN state */
+-      if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
++      if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
++              return HRTIMER_NORESTART;
++
++      /* only claim the timeout if the state is still unchanged */
++      if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
+               return HRTIMER_NORESTART;
+       /* we did not get any flow control or echo frame in time */
+-      /* report 'communication error on send' */
+-      sk->sk_err = ECOMM;
+-      if (!sock_flag(sk, SOCK_DEAD))
+-              sk_error_report(sk);
++      if (READ_ONCE(so->tx_gen) == gen) {
++              /* report 'communication error on send' */
++              sk->sk_err = ECOMM;
++              if (!sock_flag(sk, SOCK_DEAD))
++                      sk_error_report(sk);
++      }
+-      /* reset tx state */
+-      so->tx.state = ISOTP_IDLE;
+       wake_up_interruptible(&so->wait);
+       return HRTIMER_NORESTART;
+ }
++/* so->txtimer: fires when a Flow Control frame does not arrive in time */
++static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           txtimer);
++
++      return isotp_tx_timeout(so);
++}
++
++/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
++static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           echotimer);
++
++      return isotp_tx_timeout(so);
++}
++
+ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+ {
+       struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+                                            txfrtimer);
+       /* start echo timeout handling and cover below protocol error */
+-      hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++      hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
+       /* cfecho should be consumed by isotp_rcv_echo() here */
+@@ -920,13 +984,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+       int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
++      struct hrtimer *tx_hrt = &so->echotimer;
++      u32 new_state = ISOTP_SENDING;
+       int off;
+       int err;
+       if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+               return -EADDRNOTAVAIL;
+-      while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
++      /* claim the socket under so->rx_lock: this serializes the claim
++       * with the RX path and with sendmsg()'s own error paths below, so
++       * none of them can ever see a transfer mid-claim
++       */
++      for (;;) {
++              spin_lock_bh(&so->rx_lock);
++              if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
++                      break;
++              spin_unlock_bh(&so->rx_lock);
++
+               /* we do not support multiple buffers - for now */
+               if (msg->msg_flags & MSG_DONTWAIT)
+                       return -EAGAIN;
+@@ -935,11 +1010,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       return -EADDRNOTAVAIL;
+               /* wait for complete transmission of current pdu */
+-              err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
++              err = wait_event_interruptible(so->wait,
++                                             so->tx.state == ISOTP_IDLE);
+               if (err)
+-                      goto err_event_drop;
++                      return err;
+       }
++      /* new transfer: bump so->tx_gen and drain the old one's timers,
++       * still under the so->rx_lock we just claimed the socket with
++       */
++      WRITE_ONCE(so->tx.state, ISOTP_SENDING);
++      WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
++      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
++      hrtimer_cancel(&so->txfrtimer);
++      so->cfecho = 0;
++      spin_unlock_bh(&so->rx_lock);
++
+       /* so->bound is only checked once above - a wakeup may have
+        * unbound/rebound the socket meanwhile, so re-validate it
+        */
+@@ -1040,18 +1127,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       so->cfecho = *(u32 *)cf->data;
+               } else {
+                       /* standard flow control check */
+-                      so->tx.state = ISOTP_WAIT_FIRST_FC;
++                      new_state = ISOTP_WAIT_FIRST_FC;
+                       /* start timeout for FC */
+                       hrtimer_sec = ISOTP_FC_TIMEOUT;
++                      tx_hrt = &so->txtimer;
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
+       }
+-      hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
++      spin_lock_bh(&so->rx_lock);
++      if (so->tx.state == ISOTP_SHUTDOWN) {
++              /* isotp_release() has since taken over and already drained
++               * our timers - don't send into a socket that's going away
++               */
++              spin_unlock_bh(&so->rx_lock);
++              kfree_skb(skb);
++              dev_put(dev);
++              wake_up_interruptible(&so->wait);
++              return -EADDRNOTAVAIL;
++      }
++      /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
++      so->tx.state = new_state;
++      hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
++      spin_unlock_bh(&so->rx_lock);
+       /* send the first or only CAN frame */
+       cf->flags = so->ll.tx_flags;
+@@ -1064,13 +1166,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+               pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
+                              __func__, ERR_PTR(err));
++              spin_lock_bh(&so->rx_lock);
+               /* no transmission -> no timeout monitoring */
+-              hrtimer_cancel(&so->txtimer);
+-
+-              /* reset consecutive frame echo tag */
+-              so->cfecho = 0;
+-
+-              goto err_out_drop;
++              hrtimer_cancel(tx_hrt);
++              goto err_out_drop_locked;
+       }
+       if (wait_tx_done) {
+@@ -1086,14 +1185,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       return size;
++err_out_drop:
++      /* claimed but nothing sent yet - no timer to cancel */
++      spin_lock_bh(&so->rx_lock);
++      goto err_out_drop_locked;
+ err_event_drop:
+-      /* got signal: force tx state machine to be idle */
+-      so->tx.state = ISOTP_IDLE;
++      /* interrupted waiting on our own transfer - drain its timers */
++      spin_lock_bh(&so->rx_lock);
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
+-err_out_drop:
+-      /* drop this PDU and unlock a potential wait queue */
++      hrtimer_cancel(&so->echotimer);
++err_out_drop_locked:
++      /* release the claim; so->rx_lock still held from above */
++      so->cfecho = 0;
+       so->tx.state = ISOTP_IDLE;
++      spin_unlock_bh(&so->rx_lock);
+       wake_up_interruptible(&so->wait);
+       return err;
+@@ -1157,13 +1263,20 @@ static int isotp_release(struct socket *sock)
+       so = isotp_sk(sk);
+       net = sock_net(sk);
+-      /* wait for complete transmission of current pdu */
+-      while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
+-             cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
++      /* best-effort: wait for a running pdu to finish, but don't block on
++       * it forever - give up after the first signal
++       */
++      while (so->tx.state != ISOTP_IDLE &&
++             wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+               ;
+-      /* force state machines to be idle also when a signal occurred */
++      /* claim the socket under so->rx_lock like sendmsg() does, so its
++       * claim can't race the forced ISOTP_SHUTDOWN below; force it
++       * unconditionally, even when a signal cut the wait above short
++       */
++      spin_lock_bh(&so->rx_lock);
+       so->tx.state = ISOTP_SHUTDOWN;
++      spin_unlock_bh(&so->rx_lock);
+       so->rx.state = ISOTP_IDLE;
+       spin_lock(&isotp_notifier_lock);
+@@ -1209,6 +1322,7 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       hrtimer_cancel(&so->rxtimer);
+       sock_orphan(sk);
+@@ -1631,6 +1745,8 @@ static int isotp_init(struct sock *sk)
+       so->rxtimer.function = isotp_rx_timer_handler;
+       hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txtimer.function = isotp_tx_timer_handler;
++      hrtimer_init(&so->echotimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
++      so->echotimer.function = isotp_echo_timer_handler;
+       hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txfrtimer.function = isotp_txfr_timer_handler;
+-- 
+2.53.0
+
diff --git a/queue-5.15/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch b/queue-5.15/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
new file mode 100644 (file)
index 0000000..ca9bcd4
--- /dev/null
@@ -0,0 +1,198 @@
+From 299771156976f76a15b7dacb2829b8eb3557b84a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 14:55:37 +0200
+Subject: dmaengine: sh: rz-dmac: Move interrupt request after everything is
+ set up
+
+From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream.
+
+Once the interrupt is requested, the interrupt handler may run immediately.
+Since the IRQ handler can access channel->ch_base, which is initialized
+only after requesting the IRQ, this may lead to invalid memory access.
+Likewise, the IRQ thread may access uninitialized data (the ld_free,
+ld_queue, and ld_active lists), which may also lead to issues.
+
+Request the interrupts only after everything is set up. To keep the error
+path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent().
+
+Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
+Cc: stable@vger.kernel.org
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Tested-by: John Madieu <john.madieu.xa@bp.renesas.com>
+Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+[tm: kept channel->irq (a struct field, not a local irq variable) and
+ the sprintf()/pdev_irqname[5] pair in rz_dmac_chan_probe(), and the
+ mandatory platform_get_irq_byname() call for the error IRQ in
+ rz_dmac_probe(), since e0c51fd02f9c ("Make channel irq local"),
+ c4d6dcb3b625 ("Avoid format-overflow warning") and b34f3fcae72a
+ ("make error interrupt optional") are not yet backported to this
+ tree]
+Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sh/rz-dmac.c | 94 ++++++++++++++++------------------------
+ 1 file changed, 37 insertions(+), 57 deletions(-)
+
+diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
+index c22743e41640fd..cec94db38c550e 100644
+--- a/drivers/dma/sh/rz-dmac.c
++++ b/drivers/dma/sh/rz-dmac.c
+@@ -773,27 +773,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       channel->index = index;
+       channel->mid_rid = -EINVAL;
+-      /* Request the channel interrupt. */
+-      sprintf(pdev_irqname, "ch%u", index);
+-      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
+-      if (channel->irq < 0)
+-              return channel->irq;
+-
+-      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
+-                               dev_name(dmac->dev), index);
+-      if (!irqname)
+-              return -ENOMEM;
+-
+-      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
+-                                      rz_dmac_irq_handler,
+-                                      rz_dmac_irq_handler_thread, 0,
+-                                      irqname, channel);
+-      if (ret) {
+-              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
+-                      channel->irq, ret);
+-              return ret;
+-      }
+-
+       /* Set io base address for each channel */
+       if (index < 8) {
+               channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET +
+@@ -806,9 +785,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       }
+       /* Allocate descriptors */
+-      lmdesc = dma_alloc_coherent(&pdev->dev,
+-                                  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                  &channel->lmdesc.base_dma, GFP_KERNEL);
++      lmdesc = dmam_alloc_coherent(&pdev->dev,
++                                   sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
++                                   &channel->lmdesc.base_dma, GFP_KERNEL);
+       if (!lmdesc) {
+               dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n");
+               return -ENOMEM;
+@@ -824,7 +803,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       INIT_LIST_HEAD(&channel->ld_free);
+       INIT_LIST_HEAD(&channel->ld_active);
+-      return 0;
++      /* Request the channel interrupt. */
++      sprintf(pdev_irqname, "ch%u", index);
++      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
++      if (channel->irq < 0)
++              return channel->irq;
++
++      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
++                               dev_name(dmac->dev), index);
++      if (!irqname)
++              return -ENOMEM;
++
++      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
++                                      rz_dmac_irq_handler,
++                                      rz_dmac_irq_handler_thread, 0,
++                                      irqname, channel);
++      if (ret)
++              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
++                      channel->irq, ret);
++
++      return ret;
+ }
+ static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
+@@ -851,7 +849,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       const char *irqname = "error";
+       struct dma_device *engine;
+       struct rz_dmac *dmac;
+-      int channel_num;
+       unsigned int i;
+       int ret;
+       int irq;
+@@ -881,26 +878,28 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       if (IS_ERR(dmac->ext_base))
+               return PTR_ERR(dmac->ext_base);
++      /* Initialize the channels. */
++      INIT_LIST_HEAD(&dmac->engine.channels);
++
++      for (i = 0; i < dmac->n_channels; i++) {
++              ret = rz_dmac_chan_probe(dmac, &dmac->channels[i], i);
++              if (ret < 0)
++                      goto err;
++      }
++
+       /* Register interrupt handler for error */
+       irq = platform_get_irq_byname(pdev, irqname);
+-      if (irq < 0)
+-              return irq;
++      if (irq < 0) {
++              ret = irq;
++              goto err;
++      }
+       ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
+                              irqname, NULL);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
+                       irq, ret);
+-              return ret;
+-      }
+-
+-      /* Initialize the channels. */
+-      INIT_LIST_HEAD(&dmac->engine.channels);
+-
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              ret = rz_dmac_chan_probe(dmac, &dmac->channels[i], i);
+-              if (ret < 0)
+-                      goto err;
++              goto err;
+       }
+       /* Register the DMAC as a DMA provider for DT. */
+@@ -940,32 +939,13 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ dma_register_err:
+       of_dma_controller_free(pdev->dev.of_node);
+ err:
+-      channel_num = i ? i - 1 : 0;
+-      for (i = 0; i < channel_num; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+-
+       return ret;
+ }
+ static int rz_dmac_remove(struct platform_device *pdev)
+ {
+       struct rz_dmac *dmac = platform_get_drvdata(pdev);
+-      unsigned int i;
+-
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+       of_dma_controller_free(pdev->dev.of_node);
+       dma_async_device_unregister(&dmac->engine);
+-- 
+2.53.0
+
diff --git a/queue-5.15/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-5.15/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..a247a62
--- /dev/null
@@ -0,0 +1,114 @@
+From 3ef0a64dbf7ab736493532d4ec8693939553f035 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 59055818e0e6fc..b4e7dd8a993737 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-5.15/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-5.15/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..8ea95dc
--- /dev/null
@@ -0,0 +1,42 @@
+From f14ab5e1e9d3b5be7e37f5f94fcbe63f2af0590c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index b4e7dd8a993737..6b1d8f92a9bb06 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
index 2f0538a2015d75dc24f116f65b4450fe5b400116..18bca0a39a39fc45d4f4bb475094a3f0e045e40c 100644 (file)
@@ -3,3 +3,8 @@ futex-prevent-lockup-in-requeue-pi-during-signal-tim.patch
 macsec-don-t-read-an-unset-mac-header-in-macsec_encr.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.patch
+can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
+can-isotp-serialize-tx-state-transitions-under-so-rx.patch
+dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
diff --git a/queue-6.1/can-bcm-add-locking-when-updating-filter-and-timer-v.patch b/queue-6.1/can-bcm-add-locking-when-updating-filter-and-timer-v.patch
new file mode 100644 (file)
index 0000000..ccd3577
--- /dev/null
@@ -0,0 +1,412 @@
+From a8d3d63294bf736b4ed677704ea2233d3c179158 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:22 +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 | 176 ++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 133 insertions(+), 43 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 9fc733b54a86a0..5673cda3df2c62 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)) {
++      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);
+-              return HRTIMER_RESTART;
++              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;
+@@ -691,12 +721,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);
+@@ -730,6 +771,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);
+ }
+ /*
+@@ -1073,7 +1116,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);
+@@ -1087,6 +1130,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)
+  */
+@@ -1121,6 +1182,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 */
+               /*
+@@ -1132,19 +1195,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;
+@@ -1155,6 +1247,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);
+@@ -1196,6 +1289,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 */
+@@ -1223,29 +1318,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)
+@@ -1255,9 +1343,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
+
diff --git a/queue-6.1/can-bcm-add-missing-device-refcount-for-can-filter-r.patch b/queue-6.1/can-bcm-add-missing-device-refcount-for-can-filter-r.patch
new file mode 100644 (file)
index 0000000..2850d24
--- /dev/null
@@ -0,0 +1,121 @@
+From 06ff2decc1e2601f7e72e2ba61800bfdc3d08542 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:26 +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 0c8e1b58770582..5f289388d261e3 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -889,6 +889,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);
+@@ -919,17 +920,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,
+@@ -1452,7 +1450,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
+@@ -1825,16 +1831,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
+
diff --git a/queue-6.1/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch b/queue-6.1/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
new file mode 100644 (file)
index 0000000..b8cc706
--- /dev/null
@@ -0,0 +1,230 @@
+From 12c6352112d4e36865e4a1403d20271aaa57b90f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:24 +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 be74888c64c6f0..953b62965466f3 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);
+       }
+@@ -988,6 +1016,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 */
+               /*
+@@ -998,11 +1028,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)
+@@ -1012,36 +1054,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 */
+@@ -1118,10 +1162,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
+
diff --git a/queue-6.1/can-bcm-fix-can-frame-rx-tx-statistics.patch b/queue-6.1/can-bcm-fix-can-frame-rx-tx-statistics.patch
new file mode 100644 (file)
index 0000000..ac48cd1
--- /dev/null
@@ -0,0 +1,191 @@
+From 5f5ca067c97e7c7a7c88deba16f8da61e8a1819c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:23 +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 5673cda3df2c62..be74888c64c6f0 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);
+@@ -718,25 +737,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
+
diff --git a/queue-6.1/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch b/queue-6.1/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
new file mode 100644 (file)
index 0000000..7f805b6
--- /dev/null
@@ -0,0 +1,74 @@
+From 3338e0cdfd17729b6f34b80db863e5cd6a744e9d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:28 +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 c304279d667b4f..e6eaf7dfcb2ac5 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -761,11 +761,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);
+@@ -789,6 +784,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
+
diff --git a/queue-6.1/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch b/queue-6.1/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
new file mode 100644 (file)
index 0000000..1f998d1
--- /dev/null
@@ -0,0 +1,164 @@
+From d907329fb8da4ee2b9b73e558642700429adbd01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:27 +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 5f289388d261e3..c304279d667b4f 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1239,6 +1239,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))) {
+@@ -1323,8 +1324,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 */
+@@ -1394,6 +1402,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))) */
+@@ -1407,7 +1416,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;
+@@ -1437,7 +1446,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;
+@@ -1467,18 +1479,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;
+@@ -1694,11 +1720,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
+
diff --git a/queue-6.1/can-bcm-track-a-single-source-interface-for-anydev-t.patch b/queue-6.1/can-bcm-track-a-single-source-interface-for-anydev-t.patch
new file mode 100644 (file)
index 0000000..3c4bb6d
--- /dev/null
@@ -0,0 +1,143 @@
+From 1a4fc84f1ff2ec9bc3873c9554f56bcbc379d187 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:29 +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 e6eaf7dfcb2ac5..16640650c43384 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;
+@@ -758,6 +759,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       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);
+@@ -784,10 +812,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;
+@@ -1321,6 +1348,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);
+@@ -1724,10 +1752,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
+
diff --git a/queue-6.1/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch b/queue-6.1/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
new file mode 100644 (file)
index 0000000..2c1bd90
--- /dev/null
@@ -0,0 +1,128 @@
+From 3c2f418d15b25958881d0bbdbfb138f1060553ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:23:25 +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 953b62965466f3..0c8e1b58770582 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1199,22 +1199,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;
+ }
+ /*
+@@ -1277,7 +1292,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);
+@@ -1350,16 +1369,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 */
+@@ -1461,6 +1476,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
+
diff --git a/queue-6.1/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch b/queue-6.1/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
new file mode 100644 (file)
index 0000000..f056f61
--- /dev/null
@@ -0,0 +1,238 @@
+From 6b79050cbe96ba501db9df10a87cdb8f8444d134 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:33:36 +0200
+Subject: can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream.
+
+isotp_release() looked up the bound network device via dev_get_by_index()
+using the stored ifindex. During device unregistration the device is
+unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier
+chain runs, so a concurrent isotp_release() could find no device, skip
+can_rx_unregister() entirely, and still proceed to free the socket.
+Since isotp_release() had already removed itself from the isotp
+notifier list at that point, isotp_notify() would never get a chance to
+clean up either, leaving a stale CAN filter that keeps pointing at the
+freed socket.
+
+Fix this the same way raw.c already does: hold a tracked reference to
+the bound net_device in the socket (so->dev/so->dev_tracker) from
+bind() onward instead of re-resolving it from the ifindex, and
+serialize bind()/release() with rtnl_lock() so that so->dev is always
+consistent with what the NETDEV_UNREGISTER notifier sees. so->dev
+stays valid regardless of ifindex-hash unlisting, and is only ever
+cleared by whichever of isotp_release()/isotp_notify() gets there
+first, so the filter is always removed exactly once.
+
+isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state
+isn't ISOTP_IDLE yet, so a timer left running by a prior
+NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks
+share the same lock_sock() section, so there is no window in which a
+concurrent isotp_notify() clearing so->bound could be missed.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@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/isotp.c | 88 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 59 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 7d9327b3211078..23affae810dd0a 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -137,6 +137,7 @@ struct isotp_sock {
+       struct sock sk;
+       int bound;
+       int ifindex;
++      struct net_device *dev;
+       canid_t txid;
+       canid_t rxid;
+       ktime_t tx_gap;
+@@ -939,6 +940,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       goto err_event_drop;
+       }
++      /* so->bound is only checked once above - a wakeup may have
++       * unbound/rebound the socket meanwhile, so re-validate it
++       */
++      if (!so->bound) {
++              err = -EADDRNOTAVAIL;
++              goto err_out_drop;
++      }
++
+       if (!size || size > MAX_MSG_LENGTH) {
+               err = -EINVAL;
+               goto err_out_drop;
+@@ -1164,28 +1173,30 @@ static int isotp_release(struct socket *sock)
+       list_del(&so->notifier);
+       spin_unlock(&isotp_notifier_lock);
++      rtnl_lock();
+       lock_sock(sk);
+-      /* remove current filters & unregister */
+-      if (so->bound) {
+-              if (so->ifindex) {
+-                      struct net_device *dev;
+-
+-                      dev = dev_get_by_index(net, so->ifindex);
+-                      if (dev) {
+-                              if (isotp_register_rxid(so))
+-                                      can_rx_unregister(net, dev, so->rxid,
+-                                                        SINGLE_MASK(so->rxid),
+-                                                        isotp_rcv, sk);
+-
+-                              can_rx_unregister(net, dev, so->txid,
+-                                                SINGLE_MASK(so->txid),
+-                                                isotp_rcv_echo, sk);
+-                              dev_put(dev);
+-                      }
+-              }
++      /* remove current filters & unregister
++       * tracked reference so->dev is taken at bind() time with rtnl_lock
++       */
++      if (so->bound && so->dev) {
++              if (isotp_register_rxid(so))
++                      can_rx_unregister(net, so->dev, so->rxid,
++                                        SINGLE_MASK(so->rxid),
++                                        isotp_rcv, sk);
++
++              can_rx_unregister(net, so->dev, so->txid,
++                                SINGLE_MASK(so->txid),
++                                isotp_rcv_echo, sk);
++              dev_put(so->dev);
+       }
++      so->ifindex = 0;
++      so->bound = 0;
++      so->dev = NULL;
++
++      rtnl_unlock();
++
+       /* Always wait for a grace period before touching the timers below.
+        * A concurrent NETDEV_UNREGISTER may have already unregistered our
+        * filters and cleared so->bound in isotp_notify() without waiting
+@@ -1198,9 +1209,6 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txtimer);
+       hrtimer_cancel(&so->rxtimer);
+-      so->ifindex = 0;
+-      so->bound = 0;
+-
+       sock_orphan(sk);
+       sock->sk = NULL;
+@@ -1254,6 +1262,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       if (!addr->can_ifindex)
+               return -ENODEV;
++      rtnl_lock();
+       lock_sock(sk);
+       if (so->bound) {
+@@ -1261,6 +1270,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
++      /* A transmission or reception that outlived a previous binding
++       * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
++       * and RX watchdog timers bound how long this takes. Checked together
++       * with so->bound in the same lock_sock() section above, so there is
++       * no window in which a concurrent isotp_notify() could be missed.
++       */
++      if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
++              err = -EAGAIN;
++              goto out;
++      }
++
+       /* ensure different CAN IDs when the rx_id is to be registered */
+       if (isotp_register_rxid(so) && rx_id == tx_id) {
+               err = -EADDRNOTAVAIL;
+@@ -1273,14 +1293,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
+       if (dev->type != ARPHRD_CAN) {
+-              dev_put(dev);
+               err = -ENODEV;
+-              goto out;
++              goto out_put_dev;
+       }
+-      if (dev->mtu < so->ll.mtu) {
+-              dev_put(dev);
++      if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+               err = -EINVAL;
+-              goto out;
++              goto out_put_dev;
+       }
+       if (!(dev->flags & IFF_UP))
+               notify_enetdown = 1;
+@@ -1298,16 +1316,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
+                       isotp_rcv_echo, sk, "isotpe", sk);
+-      dev_put(dev);
+-
+       /* switch to new settings */
+       so->ifindex = ifindex;
+       so->rxid = rx_id;
+       so->txid = tx_id;
+       so->bound = 1;
++      /* bind() ok -> hold a reference for so->dev so that isotp_release()
++       * can safely reach the device later, even if a concurrent
++       * NETDEV_UNREGISTER has already unlisted it by ifindex.
++       */
++      so->dev = dev;
++      dev_hold(so->dev);
++
++out_put_dev:
++      /* remove potential reference from dev_get_by_index() */
++      dev_put(dev);
+ out:
+       release_sock(sk);
++      rtnl_unlock();
+       if (notify_enetdown) {
+               sk->sk_err = ENETDOWN;
+@@ -1510,7 +1537,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return;
+-      if (so->ifindex != dev->ifindex)
++      if (so->dev != dev)
+               return;
+       switch (msg) {
+@@ -1526,10 +1553,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+                       can_rx_unregister(dev_net(dev), dev, so->txid,
+                                         SINGLE_MASK(so->txid),
+                                         isotp_rcv_echo, sk);
++                      dev_put(so->dev);
+               }
+               so->ifindex = 0;
+               so->bound  = 0;
++              so->dev = NULL;
+               release_sock(sk);
+               sk->sk_err = ENODEV;
+@@ -1574,6 +1603,7 @@ static int isotp_init(struct sock *sk)
+       so->ifindex = 0;
+       so->bound = 0;
++      so->dev = NULL;
+       so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+       so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+-- 
+2.53.0
+
diff --git a/queue-6.1/can-isotp-serialize-tx-state-transitions-under-so-rx.patch b/queue-6.1/can-isotp-serialize-tx-state-transitions-under-so-rx.patch
new file mode 100644 (file)
index 0000000..0fb725a
--- /dev/null
@@ -0,0 +1,426 @@
+From a9f5e019b0df179b5580885a59eaeb9cd01eccc9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:33:37 +0200
+Subject: can: isotp: serialize TX state transitions under so->rx_lock
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream.
+
+The TX state machine (so->tx.state) is driven from three contexts:
+sendmsg() claiming and progressing a transfer, the RX path consuming
+Flow Control/echo frames, and two hrtimers timing out a stalled
+transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with
+hrtimer_cancel() calls made under so->rx_lock elsewhere left windows
+where a frame or timer callback could act on a state that had already
+moved on, corrupting an unrelated transfer.
+
+so->rx_lock now covers the full lifecycle of a TX claim: sendmsg()
+takes it to check so->tx.state is ISOTP_IDLE, switch it to
+ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's
+timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf()
+already run under this lock via isotp_rcv(), and isotp_rcv_echo() now
+takes it itself, so none of them can ever observe a transfer mid-claim.
+This also means a transfer can no longer be handed to sendmsg()'s
+cleanup paths (signal or send error) while another thread is
+concurrently claiming or finishing it, so those paths can cancel
+timers and reset the state unconditionally.
+
+isotp_release() claims the socket the same way, so a racing sendmsg()
+sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending.
+
+Only the hrtimer callbacks stay outside so->rx_lock, since they run
+under so->rx_lock's cancellation elsewhere and taking it themselves
+would deadlock. so->tx_gen lets them recognize whether the transfer
+they timed out is still the one currently active, so they don't
+report an error against a transfer that has since completed or been
+superseded.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@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/isotp.c | 192 ++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 154 insertions(+), 38 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 23affae810dd0a..6ee4849e4b789a 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -142,7 +142,7 @@ struct isotp_sock {
+       canid_t rxid;
+       ktime_t tx_gap;
+       ktime_t lastrxcf_tstamp;
+-      struct hrtimer rxtimer, txtimer, txfrtimer;
++      struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
+       struct can_isotp_options opt;
+       struct can_isotp_fc_options rxfc, txfc;
+       struct can_isotp_ll_options ll;
+@@ -150,6 +150,7 @@ struct isotp_sock {
+       u32 force_tx_stmin;
+       u32 force_rx_stmin;
+       u32 cfecho; /* consecutive frame echo tag */
++      u32 tx_gen; /* generation, bumped per new tx transfer */
+       struct tpcon rx, tx;
+       struct list_head notifier;
+       wait_queue_head_t wait;
+@@ -356,6 +357,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+       hrtimer_cancel(&so->txtimer);
++      /* isotp_tx_timeout() may have given up on this job while
++       * hrtimer_cancel() above waited for it to finish; so->rx_lock
++       * (held by our caller isotp_rcv()) rules out a concurrent claim,
++       * so a plain recheck is enough here.
++       */
++      if (so->tx.state != ISOTP_WAIT_FC &&
++          so->tx.state != ISOTP_WAIT_FIRST_FC)
++              return 1;
++
+       if ((cf->len < ae + FC_CONTENT_SZ) ||
+           ((so->opt.flags & ISOTP_CHECK_PADDING) &&
+            check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
+@@ -401,7 +411,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+               so->tx.bs = 0;
+               so->tx.state = ISOTP_SENDING;
+               /* send CF frame and enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+               break;
+@@ -544,6 +554,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
+       hrtimer_cancel(&so->rxtimer);
++      /* isotp_rx_timer_handler() may have raced us for so->rx.state
++       * while hrtimer_cancel() above waited for it to finish, already
++       * reporting ETIMEDOUT and resetting the reception; don't process
++       * this CF into a reassembly that has already been given up on.
++       */
++      if (so->rx.state != ISOTP_WAIT_DATA)
++              return 1;
++
+       /* CFs are never longer than the FF */
+       if (cf->len > so->rx.ll_dl)
+               return 1;
+@@ -833,20 +851,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       /* only handle my own local echo CF/SF skb's (no FF!) */
+-      if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
++      if (skb->sk != sk)
+               return;
++      /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
++       * (no isotp_rcv() caller here), so take it ourselves
++       */
++      spin_lock(&so->rx_lock);
++
++      /* so->cfecho may since belong to a new transfer; recheck under lock */
++      if (so->cfecho != *(u32 *)cf->data)
++              goto out_unlock;
++
+       /* cancel local echo timeout */
+-      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       /* local echo skb with consecutive frame has been consumed */
+       so->cfecho = 0;
++      /* claiming a transfer also takes so->rx_lock, so a plain recheck
++       * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
++       * a new claim while we're still in here
++       */
++      if (so->tx.state != ISOTP_SENDING)
++              goto out_unlock;
++
+       if (so->tx.idx >= so->tx.len) {
+               /* we are done */
+               so->tx.state = ISOTP_IDLE;
+               wake_up_interruptible(&so->wait);
+-              return;
++              goto out_unlock;
+       }
+       if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+@@ -854,53 +888,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+               so->tx.state = ISOTP_WAIT_FC;
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+-              return;
++              goto out_unlock;
+       }
+       /* no gap between data frames needed => use burst mode */
+       if (!so->tx_gap) {
+               /* enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+-              return;
++              goto out_unlock;
+       }
+       /* start timer to send next consecutive frame with correct delay */
+       hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
++
++out_unlock:
++      spin_unlock(&so->rx_lock);
+ }
+-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
++ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
++ * a new transfer with an error from the one that just timed out.
++ */
++static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
+ {
+-      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+-                                           txtimer);
+       struct sock *sk = &so->sk;
++      u32 gen = READ_ONCE(so->tx_gen);
++      u32 old_state = READ_ONCE(so->tx.state);
+       /* don't handle timeouts in IDLE or SHUTDOWN state */
+-      if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
++      if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
++              return HRTIMER_NORESTART;
++
++      /* only claim the timeout if the state is still unchanged */
++      if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
+               return HRTIMER_NORESTART;
+       /* we did not get any flow control or echo frame in time */
+-      /* report 'communication error on send' */
+-      sk->sk_err = ECOMM;
+-      if (!sock_flag(sk, SOCK_DEAD))
+-              sk_error_report(sk);
++      if (READ_ONCE(so->tx_gen) == gen) {
++              /* report 'communication error on send' */
++              sk->sk_err = ECOMM;
++              if (!sock_flag(sk, SOCK_DEAD))
++                      sk_error_report(sk);
++      }
+-      /* reset tx state */
+-      so->tx.state = ISOTP_IDLE;
+       wake_up_interruptible(&so->wait);
+       return HRTIMER_NORESTART;
+ }
++/* so->txtimer: fires when a Flow Control frame does not arrive in time */
++static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           txtimer);
++
++      return isotp_tx_timeout(so);
++}
++
++/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
++static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           echotimer);
++
++      return isotp_tx_timeout(so);
++}
++
+ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+ {
+       struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+                                            txfrtimer);
+       /* start echo timeout handling and cover below protocol error */
+-      hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++      hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
+       /* cfecho should be consumed by isotp_rcv_echo() here */
+@@ -920,13 +984,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+       int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
++      struct hrtimer *tx_hrt = &so->echotimer;
++      u32 new_state = ISOTP_SENDING;
+       int off;
+       int err;
+       if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+               return -EADDRNOTAVAIL;
+-      while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
++      /* claim the socket under so->rx_lock: this serializes the claim
++       * with the RX path and with sendmsg()'s own error paths below, so
++       * none of them can ever see a transfer mid-claim
++       */
++      for (;;) {
++              spin_lock_bh(&so->rx_lock);
++              if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
++                      break;
++              spin_unlock_bh(&so->rx_lock);
++
+               /* we do not support multiple buffers - for now */
+               if (msg->msg_flags & MSG_DONTWAIT)
+                       return -EAGAIN;
+@@ -935,11 +1010,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       return -EADDRNOTAVAIL;
+               /* wait for complete transmission of current pdu */
+-              err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
++              err = wait_event_interruptible(so->wait,
++                                             so->tx.state == ISOTP_IDLE);
+               if (err)
+-                      goto err_event_drop;
++                      return err;
+       }
++      /* new transfer: bump so->tx_gen and drain the old one's timers,
++       * still under the so->rx_lock we just claimed the socket with
++       */
++      WRITE_ONCE(so->tx.state, ISOTP_SENDING);
++      WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
++      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
++      hrtimer_cancel(&so->txfrtimer);
++      so->cfecho = 0;
++      spin_unlock_bh(&so->rx_lock);
++
+       /* so->bound is only checked once above - a wakeup may have
+        * unbound/rebound the socket meanwhile, so re-validate it
+        */
+@@ -1040,18 +1127,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       so->cfecho = *(u32 *)cf->data;
+               } else {
+                       /* standard flow control check */
+-                      so->tx.state = ISOTP_WAIT_FIRST_FC;
++                      new_state = ISOTP_WAIT_FIRST_FC;
+                       /* start timeout for FC */
+                       hrtimer_sec = ISOTP_FC_TIMEOUT;
++                      tx_hrt = &so->txtimer;
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
+       }
+-      hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
++      spin_lock_bh(&so->rx_lock);
++      if (so->tx.state == ISOTP_SHUTDOWN) {
++              /* isotp_release() has since taken over and already drained
++               * our timers - don't send into a socket that's going away
++               */
++              spin_unlock_bh(&so->rx_lock);
++              kfree_skb(skb);
++              dev_put(dev);
++              wake_up_interruptible(&so->wait);
++              return -EADDRNOTAVAIL;
++      }
++      /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
++      so->tx.state = new_state;
++      hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
++      spin_unlock_bh(&so->rx_lock);
+       /* send the first or only CAN frame */
+       cf->flags = so->ll.tx_flags;
+@@ -1064,13 +1166,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+               pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
+                              __func__, ERR_PTR(err));
++              spin_lock_bh(&so->rx_lock);
+               /* no transmission -> no timeout monitoring */
+-              hrtimer_cancel(&so->txtimer);
+-
+-              /* reset consecutive frame echo tag */
+-              so->cfecho = 0;
+-
+-              goto err_out_drop;
++              hrtimer_cancel(tx_hrt);
++              goto err_out_drop_locked;
+       }
+       if (wait_tx_done) {
+@@ -1086,14 +1185,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       return size;
++err_out_drop:
++      /* claimed but nothing sent yet - no timer to cancel */
++      spin_lock_bh(&so->rx_lock);
++      goto err_out_drop_locked;
+ err_event_drop:
+-      /* got signal: force tx state machine to be idle */
+-      so->tx.state = ISOTP_IDLE;
++      /* interrupted waiting on our own transfer - drain its timers */
++      spin_lock_bh(&so->rx_lock);
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
+-err_out_drop:
+-      /* drop this PDU and unlock a potential wait queue */
++      hrtimer_cancel(&so->echotimer);
++err_out_drop_locked:
++      /* release the claim; so->rx_lock still held from above */
++      so->cfecho = 0;
+       so->tx.state = ISOTP_IDLE;
++      spin_unlock_bh(&so->rx_lock);
+       wake_up_interruptible(&so->wait);
+       return err;
+@@ -1155,13 +1261,20 @@ static int isotp_release(struct socket *sock)
+       so = isotp_sk(sk);
+       net = sock_net(sk);
+-      /* wait for complete transmission of current pdu */
+-      while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
+-             cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
++      /* best-effort: wait for a running pdu to finish, but don't block on
++       * it forever - give up after the first signal
++       */
++      while (so->tx.state != ISOTP_IDLE &&
++             wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+               ;
+-      /* force state machines to be idle also when a signal occurred */
++      /* claim the socket under so->rx_lock like sendmsg() does, so its
++       * claim can't race the forced ISOTP_SHUTDOWN below; force it
++       * unconditionally, even when a signal cut the wait above short
++       */
++      spin_lock_bh(&so->rx_lock);
+       so->tx.state = ISOTP_SHUTDOWN;
++      spin_unlock_bh(&so->rx_lock);
+       so->rx.state = ISOTP_IDLE;
+       spin_lock(&isotp_notifier_lock);
+@@ -1207,6 +1320,7 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       hrtimer_cancel(&so->rxtimer);
+       sock_orphan(sk);
+@@ -1629,6 +1743,8 @@ static int isotp_init(struct sock *sk)
+       so->rxtimer.function = isotp_rx_timer_handler;
+       hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txtimer.function = isotp_tx_timer_handler;
++      hrtimer_init(&so->echotimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
++      so->echotimer.function = isotp_echo_timer_handler;
+       hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txfrtimer.function = isotp_txfr_timer_handler;
+-- 
+2.53.0
+
diff --git a/queue-6.1/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch b/queue-6.1/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
new file mode 100644 (file)
index 0000000..2ff00a5
--- /dev/null
@@ -0,0 +1,201 @@
+From 83c144a22ca97a776f78ac038f1afee0a74e58a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 14:58:13 +0200
+Subject: dmaengine: sh: rz-dmac: Move interrupt request after everything is
+ set up
+
+From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream.
+
+Once the interrupt is requested, the interrupt handler may run immediately.
+Since the IRQ handler can access channel->ch_base, which is initialized
+only after requesting the IRQ, this may lead to invalid memory access.
+Likewise, the IRQ thread may access uninitialized data (the ld_free,
+ld_queue, and ld_active lists), which may also lead to issues.
+
+Request the interrupts only after everything is set up. To keep the error
+path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent().
+
+Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
+Cc: stable@vger.kernel.org
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Tested-by: John Madieu <john.madieu.xa@bp.renesas.com>
+Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org
+[tm: Kept the channel->irq field in rz_dmac_chan_probe() instead of
+ upstream's local `irq` variable, as commit 04e227718ab8
+ ("dmaengine: sh: rz-dmac: Make channel irq local") is not present
+ in this tree. Likewise kept platform_get_irq_byname() instead of
+ platform_get_irq_byname_optional() for the error IRQ in rz_dmac_probe(),
+ as commit 6b3a6b6dc074 ("dmaengine: sh: rz_dmac: make error interrupt
+ optional") is not present in this tree either; its early return on
+ failure becomes a goto err jump to match the new call order.]
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sh/rz-dmac.c | 96 ++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 58 deletions(-)
+
+diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
+index 498e6e24ab0a37..b956292e861940 100644
+--- a/drivers/dma/sh/rz-dmac.c
++++ b/drivers/dma/sh/rz-dmac.c
+@@ -779,27 +779,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       channel->index = index;
+       channel->mid_rid = -EINVAL;
+-      /* Request the channel interrupt. */
+-      sprintf(pdev_irqname, "ch%u", index);
+-      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
+-      if (channel->irq < 0)
+-              return channel->irq;
+-
+-      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
+-                               dev_name(dmac->dev), index);
+-      if (!irqname)
+-              return -ENOMEM;
+-
+-      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
+-                                      rz_dmac_irq_handler,
+-                                      rz_dmac_irq_handler_thread, 0,
+-                                      irqname, channel);
+-      if (ret) {
+-              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
+-                      channel->irq, ret);
+-              return ret;
+-      }
+-
+       /* Set io base address for each channel */
+       if (index < 8) {
+               channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET +
+@@ -812,9 +791,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       }
+       /* Allocate descriptors */
+-      lmdesc = dma_alloc_coherent(&pdev->dev,
+-                                  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                  &channel->lmdesc.base_dma, GFP_KERNEL);
++      lmdesc = dmam_alloc_coherent(&pdev->dev,
++                                   sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
++                                   &channel->lmdesc.base_dma, GFP_KERNEL);
+       if (!lmdesc) {
+               dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n");
+               return -ENOMEM;
+@@ -830,7 +809,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       INIT_LIST_HEAD(&channel->ld_free);
+       INIT_LIST_HEAD(&channel->ld_active);
+-      return 0;
++      /* Request the channel interrupt. */
++      sprintf(pdev_irqname, "ch%u", index);
++      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
++      if (channel->irq < 0)
++              return channel->irq;
++
++      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
++                               dev_name(dmac->dev), index);
++      if (!irqname)
++              return -ENOMEM;
++
++      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
++                                      rz_dmac_irq_handler,
++                                      rz_dmac_irq_handler_thread, 0,
++                                      irqname, channel);
++      if (ret)
++              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
++                      channel->irq, ret);
++
++      return ret;
+ }
+ static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
+@@ -857,7 +855,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       const char *irqname = "error";
+       struct dma_device *engine;
+       struct rz_dmac *dmac;
+-      int channel_num;
+       unsigned int i;
+       int ret;
+       int irq;
+@@ -887,19 +884,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       if (IS_ERR(dmac->ext_base))
+               return PTR_ERR(dmac->ext_base);
+-      /* Register interrupt handler for error */
+-      irq = platform_get_irq_byname(pdev, irqname);
+-      if (irq < 0)
+-              return irq;
+-
+-      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
+-                             irqname, NULL);
+-      if (ret) {
+-              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
+-                      irq, ret);
+-              return ret;
+-      }
+-
+       /* Initialize the channels. */
+       INIT_LIST_HEAD(&dmac->engine.channels);
+@@ -916,6 +900,21 @@ static int rz_dmac_probe(struct platform_device *pdev)
+                       goto err;
+       }
++      /* Register interrupt handler for error */
++      irq = platform_get_irq_byname(pdev, irqname);
++      if (irq < 0) {
++              ret = irq;
++              goto err;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
++                             irqname, NULL);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
++                      irq, ret);
++              goto err;
++      }
++
+       /* Register the DMAC as a DMA provider for DT. */
+       ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate,
+                                        NULL);
+@@ -954,16 +953,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ dma_register_err:
+       of_dma_controller_free(pdev->dev.of_node);
+ err:
+-      channel_num = i ? i - 1 : 0;
+-      for (i = 0; i < channel_num; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+-
+       pm_runtime_put(&pdev->dev);
+ err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+@@ -974,16 +963,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ static int rz_dmac_remove(struct platform_device *pdev)
+ {
+       struct rz_dmac *dmac = platform_get_drvdata(pdev);
+-      unsigned int i;
+-
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+       of_dma_controller_free(pdev->dev.of_node);
+       dma_async_device_unregister(&dmac->engine);
+       pm_runtime_put(&pdev->dev);
+-- 
+2.53.0
+
diff --git a/queue-6.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch b/queue-6.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
new file mode 100644 (file)
index 0000000..1f60f81
--- /dev/null
@@ -0,0 +1,43 @@
+From b369d91714fdd39f7b8d17c5bc8125421a79bdc2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jun 2026 17:37:49 +0900
+Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ]
+
+__host1x_bo_unpin() drops the last reference to the mapping and frees
+it, so we can't dereference mapping afterwards. The cache itself
+outlives the mapping, so use the cache local variable instead.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/host1x/bus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
+index 819f0c4fdfa899..8c819af1bce697 100644
+--- a/drivers/gpu/host1x/bus.c
++++ b/drivers/gpu/host1x/bus.c
+@@ -1025,10 +1025,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+               if (WARN_ON(!cache))
+                       continue;
+-              mutex_lock(&mapping->cache->lock);
++              mutex_lock(&cache->lock);
+               WARN_ON(kref_read(&mapping->ref) != 1);
+               __host1x_bo_unpin(&mapping->ref);
+-              mutex_unlock(&mapping->cache->lock);
++              mutex_unlock(&cache->lock);
+       }
+ }
+ EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
+-- 
+2.53.0
+
diff --git a/queue-6.1/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-6.1/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..485bf16
--- /dev/null
@@ -0,0 +1,114 @@
+From 01c87ba3a5c53eb29afea74dd476a7df9614dd66 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 2bac9d9c7b0c9c..af1dfc08b10b25 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.1/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-6.1/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..fd40432
--- /dev/null
@@ -0,0 +1,42 @@
+From 5bcff697c8f7963a9a54dddea59b9c232d40fbab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index af1dfc08b10b25..6b2aeaa50812f2 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
index 46ac8f8c14ca2e4ee710e624d3b79044a2eed973..37eee48b055356c2230516716109f3db4b546b57 100644 (file)
@@ -7,3 +7,18 @@ seqlock-allow-ubsan_alignment-to-fail-optimizing.patch
 kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.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
+can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
+can-isotp-serialize-tx-state-transitions-under-so-rx.patch
+dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
+gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
+xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
diff --git a/queue-6.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch b/queue-6.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
new file mode 100644 (file)
index 0000000..5407cc5
--- /dev/null
@@ -0,0 +1,110 @@
+From abb66b17cbca62dc3cf93d60725f5d40f9cae71b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 10:14:04 -0400
+Subject: xprtrdma: Clear receive-side ownership pointers on release
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ]
+
+Three small ownership-state cleanups land the transport in a
+state that lets future reviewers reason about each pointer
+locally rather than tracing the whole reply path:
+
+rpcrdma_rep_put() clears rep->rr_rqst before the rep enters
+rb_free_reps so that no rep on the free list still carries a
+stale rqst pointer.  rpcrdma_reply_handler() and
+rpcrdma_unpin_rqst() are the only sites that set rr_rqst;
+rpcrdma_reply_handler() hands the rep through
+rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst
+directly because its error path abandons the rep for
+teardown cleanup rather than returning it to rb_free_reps.
+
+rpcrdma_reply_put() NULLs req->rl_reply before calling
+rpcrdma_rep_put().  The previous order placed the rep on
+rb_free_reps while req->rl_reply still pointed at it; the
+window was harmless because xprt_rdma_free_slot() holds the
+req exclusively across the pair, but closing it makes the
+invariant 'rep on rb_free_reps implies no req references it'
+strictly checkable.
+
+rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear
+req->rl_sendctx after dropping the sendctx pointer in the
+sendctx ring.  Without this, req->rl_sendctx survives across
+Send completion and points at a sendctx that may already have
+been reassigned by rpcrdma_sendctx_get_locked() to a different
+req.  No caller dereferences the stale pointer today --
+rpcrdma_prepare_send_sges() overwrites it before the next
+Send -- but a NULL is a more honest representation of 'the
+Send is no longer outstanding' and lets the assertion patch
+that follows trip on any future regression.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sunrpc/xprtrdma/rpc_rdma.c |  4 ++++
+ net/sunrpc/xprtrdma/verbs.c    | 12 ++++++++++--
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
+index e201b37578a70e..aa57e057ff451f 100644
+--- a/net/sunrpc/xprtrdma/rpc_rdma.c
++++ b/net/sunrpc/xprtrdma/rpc_rdma.c
+@@ -542,6 +542,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+       rpcrdma_req_put(req);
+ }
+@@ -550,8 +551,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+  */
+ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+ {
++      struct rpcrdma_req *req = sc->sc_req;
++
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+ }
+ /* Prepare an SGE for the RPC-over-RDMA transport header.
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index 27bb176f082f63..a97f0b18ac4294 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1067,9 +1067,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
+  * @buf: buffer pool
+  * @rep: rep to release
+  *
++ * The rep's transient association with an rpc_rqst, established
++ * by rpcrdma_reply_handler() and torn down here, must not survive
++ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
++ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
++ * would imply the rep is still referenced by a req.
+  */
+ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
+ {
++      rep->rr_rqst = NULL;
+       llist_add(&rep->rr_node, &buf->rb_free_reps);
+ }
+@@ -1252,9 +1258,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
+  */
+ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
+ {
+-      if (req->rl_reply) {
+-              rpcrdma_rep_put(buffers, req->rl_reply);
++      struct rpcrdma_rep *rep = req->rl_reply;
++
++      if (rep) {
+               req->rl_reply = NULL;
++              rpcrdma_rep_put(buffers, rep);
+       }
+       /* I2: rl_reply NULL after the put closes the
+        * 'rep on rb_free_reps still referenced by req' window.
+-- 
+2.53.0
+
diff --git a/queue-6.12/can-bcm-add-locking-when-updating-filter-and-timer-v.patch b/queue-6.12/can-bcm-add-locking-when-updating-filter-and-timer-v.patch
new file mode 100644 (file)
index 0000000..3435296
--- /dev/null
@@ -0,0 +1,417 @@
+From 3c02aa7624ea28890eb419335468bbe564358f77 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:06 +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 | 176 ++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 133 insertions(+), 43 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 03626d1ee665f1..388720ae177213 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -128,6 +128,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 {
+@@ -292,21 +293,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) {
+@@ -335,14 +342,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:
+@@ -455,7 +468,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 */
+@@ -472,7 +485,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) ?
+@@ -616,6 +629,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' */
+@@ -632,6 +647,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;
+@@ -680,15 +697,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)) {
++      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);
+-              return HRTIMER_RESTART;
++              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;
+ }
+ /*
+@@ -698,8 +726,10 @@ 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;
+       unsigned char traffic_flags;
++      bool rtr_frame;
+       if (op->can_id != rxframe->can_id)
+               return;
+@@ -723,9 +753,18 @@ 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;
+       }
+@@ -737,6 +776,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
++      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,
+@@ -772,6 +813,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);
+ }
+ /*
+@@ -1115,7 +1158,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);
+@@ -1129,6 +1172,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)
+  */
+@@ -1163,6 +1224,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 */
+               /*
+@@ -1174,19 +1237,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;
+@@ -1197,6 +1289,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);
+@@ -1238,6 +1331,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 */
+@@ -1265,29 +1360,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)
+@@ -1297,9 +1385,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
+
diff --git a/queue-6.12/can-bcm-add-missing-device-refcount-for-can-filter-r.patch b/queue-6.12/can-bcm-add-missing-device-refcount-for-can-filter-r.patch
new file mode 100644 (file)
index 0000000..9cef8e8
--- /dev/null
@@ -0,0 +1,121 @@
+From eadb23cf2f1aca55ded40ce32e97e86302b222e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:10 +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 2c12a5441eac48..e0aacfdcad8864 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -931,6 +931,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);
+@@ -961,17 +962,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,
+@@ -1494,7 +1492,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
+@@ -1867,16 +1873,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
+
diff --git a/queue-6.12/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch b/queue-6.12/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
new file mode 100644 (file)
index 0000000..3a19f6f
--- /dev/null
@@ -0,0 +1,230 @@
+From 7ffc58fcc2e3c75084d6149dc16541c7a5f373ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:08 +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 9982f1b9ac4620..e8fb31b24bb2ae 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -127,7 +127,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 */
+ };
+@@ -466,12 +466,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;
+@@ -488,25 +494,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);
+       }
+@@ -1030,6 +1058,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 */
+               /*
+@@ -1040,11 +1070,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)
+@@ -1054,36 +1096,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 */
+@@ -1160,10 +1204,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
+
diff --git a/queue-6.12/can-bcm-fix-can-frame-rx-tx-statistics.patch b/queue-6.12/can-bcm-fix-can-frame-rx-tx-statistics.patch
new file mode 100644 (file)
index 0000000..ab8df41
--- /dev/null
@@ -0,0 +1,200 @@
+From c1ff924a9a9843b585593211f1c40c1ef2b72cf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:07 +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 | 69 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 46 insertions(+), 23 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 388720ae177213..9982f1b9ac4620 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -111,7 +111,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;
+@@ -228,10 +228,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,
+@@ -253,9 +256,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);
+@@ -279,7 +282,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');
+@@ -289,6 +293,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
+@@ -340,7 +362,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
+@@ -499,12 +521,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 &= ~RX_THR;
+@@ -750,24 +769,30 @@ 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;
+       }
++      /* 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);
++
+       /* compute flags to distinguish between own/local/remote CAN traffic */
+       traffic_flags = 0;
+       if (skb->sk) {
+@@ -776,8 +801,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
+-      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,
+-- 
+2.53.0
+
diff --git a/queue-6.12/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch b/queue-6.12/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
new file mode 100644 (file)
index 0000000..8a4c187
--- /dev/null
@@ -0,0 +1,74 @@
+From 2aebcbdcb5d08eac27b766f502c2ca0e10ebe4b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:12 +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 dd176d80f4cfbf..0312b69a988c5f 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -793,11 +793,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);
+@@ -829,6 +824,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
++      /* 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
+
diff --git a/queue-6.12/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch b/queue-6.12/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
new file mode 100644 (file)
index 0000000..8e4803d
--- /dev/null
@@ -0,0 +1,164 @@
+From 331f4396db72e30bfd1e33797d933847ec61a908 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:11 +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 e0aacfdcad8864..dd176d80f4cfbf 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1281,6 +1281,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))) {
+@@ -1365,8 +1366,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 */
+@@ -1436,6 +1444,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))) */
+@@ -1449,7 +1458,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;
+@@ -1479,7 +1488,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;
+@@ -1509,18 +1521,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;
+@@ -1736,11 +1762,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
+
diff --git a/queue-6.12/can-bcm-track-a-single-source-interface-for-anydev-t.patch b/queue-6.12/can-bcm-track-a-single-source-interface-for-anydev-t.patch
new file mode 100644 (file)
index 0000000..bb78bdd
--- /dev/null
@@ -0,0 +1,143 @@
+From ae4bcabb07eb2ef6c01a00cf68957d246a4d4deb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:13 +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 0312b69a988c5f..0c7e6203eb44e2 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -116,6 +116,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;
+@@ -790,6 +791,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       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);
+@@ -824,10 +852,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
+-      /* 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;
+@@ -1363,6 +1390,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);
+@@ -1766,10 +1794,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
+
diff --git a/queue-6.12/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch b/queue-6.12/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
new file mode 100644 (file)
index 0000000..0ace64e
--- /dev/null
@@ -0,0 +1,128 @@
+From 6dde4fedd3f9d2d02417b35eb46936371e555012 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:09 +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 e8fb31b24bb2ae..2c12a5441eac48 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1241,22 +1241,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;
+ }
+ /*
+@@ -1319,7 +1334,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);
+@@ -1392,16 +1411,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 */
+@@ -1503,6 +1518,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
+
diff --git a/queue-6.12/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch b/queue-6.12/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
new file mode 100644 (file)
index 0000000..54cc3ee
--- /dev/null
@@ -0,0 +1,238 @@
+From 2eee79484627c3537f88eaee2fcc10e764a6138d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:34:54 +0200
+Subject: can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream.
+
+isotp_release() looked up the bound network device via dev_get_by_index()
+using the stored ifindex. During device unregistration the device is
+unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier
+chain runs, so a concurrent isotp_release() could find no device, skip
+can_rx_unregister() entirely, and still proceed to free the socket.
+Since isotp_release() had already removed itself from the isotp
+notifier list at that point, isotp_notify() would never get a chance to
+clean up either, leaving a stale CAN filter that keeps pointing at the
+freed socket.
+
+Fix this the same way raw.c already does: hold a tracked reference to
+the bound net_device in the socket (so->dev/so->dev_tracker) from
+bind() onward instead of re-resolving it from the ifindex, and
+serialize bind()/release() with rtnl_lock() so that so->dev is always
+consistent with what the NETDEV_UNREGISTER notifier sees. so->dev
+stays valid regardless of ifindex-hash unlisting, and is only ever
+cleared by whichever of isotp_release()/isotp_notify() gets there
+first, so the filter is always removed exactly once.
+
+isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state
+isn't ISOTP_IDLE yet, so a timer left running by a prior
+NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks
+share the same lock_sock() section, so there is no window in which a
+concurrent isotp_notify() clearing so->bound could be missed.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@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/isotp.c | 88 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 59 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 176e16b46413fa..76e84e2113eeab 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -151,6 +151,7 @@ struct isotp_sock {
+       struct sock sk;
+       int bound;
+       int ifindex;
++      struct net_device *dev;
+       canid_t txid;
+       canid_t rxid;
+       ktime_t tx_gap;
+@@ -964,6 +965,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       goto err_event_drop;
+       }
++      /* so->bound is only checked once above - a wakeup may have
++       * unbound/rebound the socket meanwhile, so re-validate it
++       */
++      if (!so->bound) {
++              err = -EADDRNOTAVAIL;
++              goto err_out_drop;
++      }
++
+       /* PDU size > default => try max_pdu_size */
+       if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) {
+               u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL);
+@@ -1199,28 +1208,30 @@ static int isotp_release(struct socket *sock)
+       list_del(&so->notifier);
+       spin_unlock(&isotp_notifier_lock);
++      rtnl_lock();
+       lock_sock(sk);
+-      /* remove current filters & unregister */
+-      if (so->bound) {
+-              if (so->ifindex) {
+-                      struct net_device *dev;
+-
+-                      dev = dev_get_by_index(net, so->ifindex);
+-                      if (dev) {
+-                              if (isotp_register_rxid(so))
+-                                      can_rx_unregister(net, dev, so->rxid,
+-                                                        SINGLE_MASK(so->rxid),
+-                                                        isotp_rcv, sk);
+-
+-                              can_rx_unregister(net, dev, so->txid,
+-                                                SINGLE_MASK(so->txid),
+-                                                isotp_rcv_echo, sk);
+-                              dev_put(dev);
+-                      }
+-              }
++      /* remove current filters & unregister
++       * tracked reference so->dev is taken at bind() time with rtnl_lock
++       */
++      if (so->bound && so->dev) {
++              if (isotp_register_rxid(so))
++                      can_rx_unregister(net, so->dev, so->rxid,
++                                        SINGLE_MASK(so->rxid),
++                                        isotp_rcv, sk);
++
++              can_rx_unregister(net, so->dev, so->txid,
++                                SINGLE_MASK(so->txid),
++                                isotp_rcv_echo, sk);
++              dev_put(so->dev);
+       }
++      so->ifindex = 0;
++      so->bound = 0;
++      so->dev = NULL;
++
++      rtnl_unlock();
++
+       /* Always wait for a grace period before touching the timers below.
+        * A concurrent NETDEV_UNREGISTER may have already unregistered our
+        * filters and cleared so->bound in isotp_notify() without waiting
+@@ -1233,9 +1244,6 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txtimer);
+       hrtimer_cancel(&so->rxtimer);
+-      so->ifindex = 0;
+-      so->bound = 0;
+-
+       sock_orphan(sk);
+       sock->sk = NULL;
+@@ -1289,6 +1297,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       if (!addr->can_ifindex)
+               return -ENODEV;
++      rtnl_lock();
+       lock_sock(sk);
+       if (so->bound) {
+@@ -1296,6 +1305,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
++      /* A transmission or reception that outlived a previous binding
++       * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
++       * and RX watchdog timers bound how long this takes. Checked together
++       * with so->bound in the same lock_sock() section above, so there is
++       * no window in which a concurrent isotp_notify() could be missed.
++       */
++      if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
++              err = -EAGAIN;
++              goto out;
++      }
++
+       /* ensure different CAN IDs when the rx_id is to be registered */
+       if (isotp_register_rxid(so) && rx_id == tx_id) {
+               err = -EADDRNOTAVAIL;
+@@ -1308,14 +1328,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
+       if (dev->type != ARPHRD_CAN) {
+-              dev_put(dev);
+               err = -ENODEV;
+-              goto out;
++              goto out_put_dev;
+       }
+-      if (dev->mtu < so->ll.mtu) {
+-              dev_put(dev);
++      if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+               err = -EINVAL;
+-              goto out;
++              goto out_put_dev;
+       }
+       if (!(dev->flags & IFF_UP))
+               notify_enetdown = 1;
+@@ -1333,16 +1351,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
+                       isotp_rcv_echo, sk, "isotpe", sk);
+-      dev_put(dev);
+-
+       /* switch to new settings */
+       so->ifindex = ifindex;
+       so->rxid = rx_id;
+       so->txid = tx_id;
+       so->bound = 1;
++      /* bind() ok -> hold a reference for so->dev so that isotp_release()
++       * can safely reach the device later, even if a concurrent
++       * NETDEV_UNREGISTER has already unlisted it by ifindex.
++       */
++      so->dev = dev;
++      dev_hold(so->dev);
++
++out_put_dev:
++      /* remove potential reference from dev_get_by_index() */
++      dev_put(dev);
+ out:
+       release_sock(sk);
++      rtnl_unlock();
+       if (notify_enetdown) {
+               sk->sk_err = ENETDOWN;
+@@ -1545,7 +1572,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return;
+-      if (so->ifindex != dev->ifindex)
++      if (so->dev != dev)
+               return;
+       switch (msg) {
+@@ -1561,10 +1588,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+                       can_rx_unregister(dev_net(dev), dev, so->txid,
+                                         SINGLE_MASK(so->txid),
+                                         isotp_rcv_echo, sk);
++                      dev_put(so->dev);
+               }
+               so->ifindex = 0;
+               so->bound  = 0;
++              so->dev = NULL;
+               release_sock(sk);
+               sk->sk_err = ENODEV;
+@@ -1624,6 +1653,7 @@ static int isotp_init(struct sock *sk)
+       so->ifindex = 0;
+       so->bound = 0;
++      so->dev = NULL;
+       so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+       so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+-- 
+2.53.0
+
diff --git a/queue-6.12/can-isotp-serialize-tx-state-transitions-under-so-rx.patch b/queue-6.12/can-isotp-serialize-tx-state-transitions-under-so-rx.patch
new file mode 100644 (file)
index 0000000..876bd91
--- /dev/null
@@ -0,0 +1,426 @@
+From 7f1b6867d956ef0f0afb0e024265a5ae4cdfb9cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:34:55 +0200
+Subject: can: isotp: serialize TX state transitions under so->rx_lock
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream.
+
+The TX state machine (so->tx.state) is driven from three contexts:
+sendmsg() claiming and progressing a transfer, the RX path consuming
+Flow Control/echo frames, and two hrtimers timing out a stalled
+transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with
+hrtimer_cancel() calls made under so->rx_lock elsewhere left windows
+where a frame or timer callback could act on a state that had already
+moved on, corrupting an unrelated transfer.
+
+so->rx_lock now covers the full lifecycle of a TX claim: sendmsg()
+takes it to check so->tx.state is ISOTP_IDLE, switch it to
+ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's
+timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf()
+already run under this lock via isotp_rcv(), and isotp_rcv_echo() now
+takes it itself, so none of them can ever observe a transfer mid-claim.
+This also means a transfer can no longer be handed to sendmsg()'s
+cleanup paths (signal or send error) while another thread is
+concurrently claiming or finishing it, so those paths can cancel
+timers and reset the state unconditionally.
+
+isotp_release() claims the socket the same way, so a racing sendmsg()
+sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending.
+
+Only the hrtimer callbacks stay outside so->rx_lock, since they run
+under so->rx_lock's cancellation elsewhere and taking it themselves
+would deadlock. so->tx_gen lets them recognize whether the transfer
+they timed out is still the one currently active, so they don't
+report an error against a transfer that has since completed or been
+superseded.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@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/isotp.c | 192 ++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 154 insertions(+), 38 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 76e84e2113eeab..b2622c881aa32b 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -156,7 +156,7 @@ struct isotp_sock {
+       canid_t rxid;
+       ktime_t tx_gap;
+       ktime_t lastrxcf_tstamp;
+-      struct hrtimer rxtimer, txtimer, txfrtimer;
++      struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
+       struct can_isotp_options opt;
+       struct can_isotp_fc_options rxfc, txfc;
+       struct can_isotp_ll_options ll;
+@@ -164,6 +164,7 @@ struct isotp_sock {
+       u32 force_tx_stmin;
+       u32 force_rx_stmin;
+       u32 cfecho; /* consecutive frame echo tag */
++      u32 tx_gen; /* generation, bumped per new tx transfer */
+       struct tpcon rx, tx;
+       struct list_head notifier;
+       wait_queue_head_t wait;
+@@ -370,6 +371,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+       hrtimer_cancel(&so->txtimer);
++      /* isotp_tx_timeout() may have given up on this job while
++       * hrtimer_cancel() above waited for it to finish; so->rx_lock
++       * (held by our caller isotp_rcv()) rules out a concurrent claim,
++       * so a plain recheck is enough here.
++       */
++      if (so->tx.state != ISOTP_WAIT_FC &&
++          so->tx.state != ISOTP_WAIT_FIRST_FC)
++              return 1;
++
+       if ((cf->len < ae + FC_CONTENT_SZ) ||
+           ((so->opt.flags & ISOTP_CHECK_PADDING) &&
+            check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
+@@ -416,7 +426,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+               so->tx.bs = 0;
+               so->tx.state = ISOTP_SENDING;
+               /* send CF frame and enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+               break;
+@@ -569,6 +579,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
+       hrtimer_cancel(&so->rxtimer);
++      /* isotp_rx_timer_handler() may have raced us for so->rx.state
++       * while hrtimer_cancel() above waited for it to finish, already
++       * reporting ETIMEDOUT and resetting the reception; don't process
++       * this CF into a reassembly that has already been given up on.
++       */
++      if (so->rx.state != ISOTP_WAIT_DATA)
++              return 1;
++
+       /* CFs are never longer than the FF */
+       if (cf->len > so->rx.ll_dl)
+               return 1;
+@@ -858,20 +876,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       /* only handle my own local echo CF/SF skb's (no FF!) */
+-      if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
++      if (skb->sk != sk)
+               return;
++      /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
++       * (no isotp_rcv() caller here), so take it ourselves
++       */
++      spin_lock(&so->rx_lock);
++
++      /* so->cfecho may since belong to a new transfer; recheck under lock */
++      if (so->cfecho != *(u32 *)cf->data)
++              goto out_unlock;
++
+       /* cancel local echo timeout */
+-      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       /* local echo skb with consecutive frame has been consumed */
+       so->cfecho = 0;
++      /* claiming a transfer also takes so->rx_lock, so a plain recheck
++       * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
++       * a new claim while we're still in here
++       */
++      if (so->tx.state != ISOTP_SENDING)
++              goto out_unlock;
++
+       if (so->tx.idx >= so->tx.len) {
+               /* we are done */
+               so->tx.state = ISOTP_IDLE;
+               wake_up_interruptible(&so->wait);
+-              return;
++              goto out_unlock;
+       }
+       if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+@@ -879,53 +913,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+               so->tx.state = ISOTP_WAIT_FC;
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+-              return;
++              goto out_unlock;
+       }
+       /* no gap between data frames needed => use burst mode */
+       if (!so->tx_gap) {
+               /* enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+-              return;
++              goto out_unlock;
+       }
+       /* start timer to send next consecutive frame with correct delay */
+       hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
++
++out_unlock:
++      spin_unlock(&so->rx_lock);
+ }
+-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
++ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
++ * a new transfer with an error from the one that just timed out.
++ */
++static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
+ {
+-      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+-                                           txtimer);
+       struct sock *sk = &so->sk;
++      u32 gen = READ_ONCE(so->tx_gen);
++      u32 old_state = READ_ONCE(so->tx.state);
+       /* don't handle timeouts in IDLE or SHUTDOWN state */
+-      if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
++      if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
++              return HRTIMER_NORESTART;
++
++      /* only claim the timeout if the state is still unchanged */
++      if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
+               return HRTIMER_NORESTART;
+       /* we did not get any flow control or echo frame in time */
+-      /* report 'communication error on send' */
+-      sk->sk_err = ECOMM;
+-      if (!sock_flag(sk, SOCK_DEAD))
+-              sk_error_report(sk);
++      if (READ_ONCE(so->tx_gen) == gen) {
++              /* report 'communication error on send' */
++              sk->sk_err = ECOMM;
++              if (!sock_flag(sk, SOCK_DEAD))
++                      sk_error_report(sk);
++      }
+-      /* reset tx state */
+-      so->tx.state = ISOTP_IDLE;
+       wake_up_interruptible(&so->wait);
+       return HRTIMER_NORESTART;
+ }
++/* so->txtimer: fires when a Flow Control frame does not arrive in time */
++static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           txtimer);
++
++      return isotp_tx_timeout(so);
++}
++
++/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
++static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           echotimer);
++
++      return isotp_tx_timeout(so);
++}
++
+ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+ {
+       struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+                                            txfrtimer);
+       /* start echo timeout handling and cover below protocol error */
+-      hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++      hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
+       /* cfecho should be consumed by isotp_rcv_echo() here */
+@@ -945,13 +1009,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+       int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
++      struct hrtimer *tx_hrt = &so->echotimer;
++      u32 new_state = ISOTP_SENDING;
+       int off;
+       int err;
+       if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+               return -EADDRNOTAVAIL;
+-      while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
++      /* claim the socket under so->rx_lock: this serializes the claim
++       * with the RX path and with sendmsg()'s own error paths below, so
++       * none of them can ever see a transfer mid-claim
++       */
++      for (;;) {
++              spin_lock_bh(&so->rx_lock);
++              if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
++                      break;
++              spin_unlock_bh(&so->rx_lock);
++
+               /* we do not support multiple buffers - for now */
+               if (msg->msg_flags & MSG_DONTWAIT)
+                       return -EAGAIN;
+@@ -960,11 +1035,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       return -EADDRNOTAVAIL;
+               /* wait for complete transmission of current pdu */
+-              err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
++              err = wait_event_interruptible(so->wait,
++                                             so->tx.state == ISOTP_IDLE);
+               if (err)
+-                      goto err_event_drop;
++                      return err;
+       }
++      /* new transfer: bump so->tx_gen and drain the old one's timers,
++       * still under the so->rx_lock we just claimed the socket with
++       */
++      WRITE_ONCE(so->tx.state, ISOTP_SENDING);
++      WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
++      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
++      hrtimer_cancel(&so->txfrtimer);
++      so->cfecho = 0;
++      spin_unlock_bh(&so->rx_lock);
++
+       /* so->bound is only checked once above - a wakeup may have
+        * unbound/rebound the socket meanwhile, so re-validate it
+        */
+@@ -1075,18 +1162,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       so->cfecho = *(u32 *)cf->data;
+               } else {
+                       /* standard flow control check */
+-                      so->tx.state = ISOTP_WAIT_FIRST_FC;
++                      new_state = ISOTP_WAIT_FIRST_FC;
+                       /* start timeout for FC */
+                       hrtimer_sec = ISOTP_FC_TIMEOUT;
++                      tx_hrt = &so->txtimer;
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
+       }
+-      hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
++      spin_lock_bh(&so->rx_lock);
++      if (so->tx.state == ISOTP_SHUTDOWN) {
++              /* isotp_release() has since taken over and already drained
++               * our timers - don't send into a socket that's going away
++               */
++              spin_unlock_bh(&so->rx_lock);
++              kfree_skb(skb);
++              dev_put(dev);
++              wake_up_interruptible(&so->wait);
++              return -EADDRNOTAVAIL;
++      }
++      /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
++      so->tx.state = new_state;
++      hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
++      spin_unlock_bh(&so->rx_lock);
+       /* send the first or only CAN frame */
+       cf->flags = so->ll.tx_flags;
+@@ -1099,13 +1201,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+               pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
+                              __func__, ERR_PTR(err));
++              spin_lock_bh(&so->rx_lock);
+               /* no transmission -> no timeout monitoring */
+-              hrtimer_cancel(&so->txtimer);
+-
+-              /* reset consecutive frame echo tag */
+-              so->cfecho = 0;
+-
+-              goto err_out_drop;
++              hrtimer_cancel(tx_hrt);
++              goto err_out_drop_locked;
+       }
+       if (wait_tx_done) {
+@@ -1121,14 +1220,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       return size;
++err_out_drop:
++      /* claimed but nothing sent yet - no timer to cancel */
++      spin_lock_bh(&so->rx_lock);
++      goto err_out_drop_locked;
+ err_event_drop:
+-      /* got signal: force tx state machine to be idle */
+-      so->tx.state = ISOTP_IDLE;
++      /* interrupted waiting on our own transfer - drain its timers */
++      spin_lock_bh(&so->rx_lock);
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
+-err_out_drop:
+-      /* drop this PDU and unlock a potential wait queue */
++      hrtimer_cancel(&so->echotimer);
++err_out_drop_locked:
++      /* release the claim; so->rx_lock still held from above */
++      so->cfecho = 0;
+       so->tx.state = ISOTP_IDLE;
++      spin_unlock_bh(&so->rx_lock);
+       wake_up_interruptible(&so->wait);
+       return err;
+@@ -1190,13 +1296,20 @@ static int isotp_release(struct socket *sock)
+       so = isotp_sk(sk);
+       net = sock_net(sk);
+-      /* wait for complete transmission of current pdu */
+-      while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
+-             cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
++      /* best-effort: wait for a running pdu to finish, but don't block on
++       * it forever - give up after the first signal
++       */
++      while (so->tx.state != ISOTP_IDLE &&
++             wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+               ;
+-      /* force state machines to be idle also when a signal occurred */
++      /* claim the socket under so->rx_lock like sendmsg() does, so its
++       * claim can't race the forced ISOTP_SHUTDOWN below; force it
++       * unconditionally, even when a signal cut the wait above short
++       */
++      spin_lock_bh(&so->rx_lock);
+       so->tx.state = ISOTP_SHUTDOWN;
++      spin_unlock_bh(&so->rx_lock);
+       so->rx.state = ISOTP_IDLE;
+       spin_lock(&isotp_notifier_lock);
+@@ -1242,6 +1355,7 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       hrtimer_cancel(&so->rxtimer);
+       sock_orphan(sk);
+@@ -1684,6 +1798,8 @@ static int isotp_init(struct sock *sk)
+       so->rxtimer.function = isotp_rx_timer_handler;
+       hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txtimer.function = isotp_tx_timer_handler;
++      hrtimer_init(&so->echotimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
++      so->echotimer.function = isotp_echo_timer_handler;
+       hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txfrtimer.function = isotp_txfr_timer_handler;
+-- 
+2.53.0
+
diff --git a/queue-6.12/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch b/queue-6.12/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
new file mode 100644 (file)
index 0000000..6cef366
--- /dev/null
@@ -0,0 +1,44 @@
+From e33be45369ff2478ba7dfa8afae516c14284e40a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:34:52 +0900
+Subject: crypto: tegra - Don't touch bo refcount in host1x bo pin/unpin
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit f8c9c57d750346abd213ffed2ae3cacb0268e9f1 ]
+
+Since commit "gpu: host1x: Allow entries in BO caches to be freed",
+host1x_bo_pin() and host1x_bo_unpin() handle the bo's refcount
+themselves. .pin/.unpin callbacks should not adjust it.
+
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/crypto/tegra/tegra-se-main.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c
+index 3c3b9f18d8cf50..e5ff4fe66a4b88 100644
+--- a/drivers/crypto/tegra/tegra-se-main.c
++++ b/drivers/crypto/tegra/tegra-se-main.c
+@@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire
+               return ERR_PTR(-ENOMEM);
+       kref_init(&map->ref);
+-      map->bo = host1x_bo_get(bo);
++      map->bo = bo;
+       map->direction = direction;
+       map->dev = dev;
+@@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map)
+       dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
+       sg_free_table(map->sgt);
+       kfree(map->sgt);
+-      host1x_bo_put(map->bo);
+       kfree(map);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch b/queue-6.12/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
new file mode 100644 (file)
index 0000000..af020ec
--- /dev/null
@@ -0,0 +1,204 @@
+From 267efe19ee69ee5e21c927ea2daef6bdf4a2a10f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:00:06 +0200
+Subject: dmaengine: sh: rz-dmac: Move interrupt request after everything is
+ set up
+
+From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream.
+
+Once the interrupt is requested, the interrupt handler may run immediately.
+Since the IRQ handler can access channel->ch_base, which is initialized
+only after requesting the IRQ, this may lead to invalid memory access.
+Likewise, the IRQ thread may access uninitialized data (the ld_free,
+ld_queue, and ld_active lists), which may also lead to issues.
+
+Request the interrupts only after everything is set up. To keep the error
+path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent().
+
+Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
+Cc: stable@vger.kernel.org
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Tested-by: John Madieu <john.madieu.xa@bp.renesas.com>
+Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org
+[tm: Kept the channel->irq field in rz_dmac_chan_probe() instead of
+ upstream's local `irq` variable, as commit 04e227718ab8
+ ("dmaengine: sh: rz-dmac: Make channel irq local") is not present
+ in this tree. Likewise kept platform_get_irq_byname() (mandatory)
+ instead of platform_get_irq_byname_optional() for the error IRQ in
+ rz_dmac_probe(), as commit 6b3a6b6dc074 ("dmaengine: sh: rz_dmac:
+ make error interrupt optional") is not present in this tree either;
+ its early return on failure becomes a goto err jump to match the
+ new call order.]
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sh/rz-dmac.c | 96 ++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 58 deletions(-)
+
+diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
+index 06952519f509b8..fece2cfec1ccb4 100644
+--- a/drivers/dma/sh/rz-dmac.c
++++ b/drivers/dma/sh/rz-dmac.c
+@@ -780,27 +780,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       channel->index = index;
+       channel->mid_rid = -EINVAL;
+-      /* Request the channel interrupt. */
+-      scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
+-      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
+-      if (channel->irq < 0)
+-              return channel->irq;
+-
+-      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
+-                               dev_name(dmac->dev), index);
+-      if (!irqname)
+-              return -ENOMEM;
+-
+-      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
+-                                      rz_dmac_irq_handler,
+-                                      rz_dmac_irq_handler_thread, 0,
+-                                      irqname, channel);
+-      if (ret) {
+-              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
+-                      channel->irq, ret);
+-              return ret;
+-      }
+-
+       /* Set io base address for each channel */
+       if (index < 8) {
+               channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET +
+@@ -813,9 +792,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       }
+       /* Allocate descriptors */
+-      lmdesc = dma_alloc_coherent(&pdev->dev,
+-                                  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                  &channel->lmdesc.base_dma, GFP_KERNEL);
++      lmdesc = dmam_alloc_coherent(&pdev->dev,
++                                   sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
++                                   &channel->lmdesc.base_dma, GFP_KERNEL);
+       if (!lmdesc) {
+               dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n");
+               return -ENOMEM;
+@@ -831,7 +810,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       INIT_LIST_HEAD(&channel->ld_free);
+       INIT_LIST_HEAD(&channel->ld_active);
+-      return 0;
++      /* Request the channel interrupt. */
++      scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
++      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
++      if (channel->irq < 0)
++              return channel->irq;
++
++      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
++                               dev_name(dmac->dev), index);
++      if (!irqname)
++              return -ENOMEM;
++
++      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
++                                      rz_dmac_irq_handler,
++                                      rz_dmac_irq_handler_thread, 0,
++                                      irqname, channel);
++      if (ret)
++              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
++                      channel->irq, ret);
++
++      return ret;
+ }
+ static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
+@@ -858,7 +856,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       const char *irqname = "error";
+       struct dma_device *engine;
+       struct rz_dmac *dmac;
+-      int channel_num;
+       int ret;
+       int irq;
+       u8 i;
+@@ -888,19 +885,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       if (IS_ERR(dmac->ext_base))
+               return PTR_ERR(dmac->ext_base);
+-      /* Register interrupt handler for error */
+-      irq = platform_get_irq_byname(pdev, irqname);
+-      if (irq < 0)
+-              return irq;
+-
+-      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
+-                             irqname, NULL);
+-      if (ret) {
+-              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
+-                      irq, ret);
+-              return ret;
+-      }
+-
+       /* Initialize the channels. */
+       INIT_LIST_HEAD(&dmac->engine.channels);
+@@ -926,6 +910,21 @@ static int rz_dmac_probe(struct platform_device *pdev)
+                       goto err;
+       }
++      /* Register interrupt handler for error */
++      irq = platform_get_irq_byname(pdev, irqname);
++      if (irq < 0) {
++              ret = irq;
++              goto err;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
++                             irqname, NULL);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
++                      irq, ret);
++              goto err;
++      }
++
+       /* Register the DMAC as a DMA provider for DT. */
+       ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate,
+                                        NULL);
+@@ -964,16 +963,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ dma_register_err:
+       of_dma_controller_free(pdev->dev.of_node);
+ err:
+-      channel_num = i ? i - 1 : 0;
+-      for (i = 0; i < channel_num; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+-
+       reset_control_assert(dmac->rstc);
+ err_pm_runtime_put:
+       pm_runtime_put(&pdev->dev);
+@@ -986,18 +975,9 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ static void rz_dmac_remove(struct platform_device *pdev)
+ {
+       struct rz_dmac *dmac = platform_get_drvdata(pdev);
+-      unsigned int i;
+       dma_async_device_unregister(&dmac->engine);
+       of_dma_controller_free(pdev->dev.of_node);
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+       reset_control_assert(dmac->rstc);
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+-- 
+2.53.0
+
diff --git a/queue-6.12/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch b/queue-6.12/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
new file mode 100644 (file)
index 0000000..02f63ae
--- /dev/null
@@ -0,0 +1,43 @@
+From 377e89f39a5ab0706619c9f117c02c14bdd170a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jun 2026 17:37:49 +0900
+Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ]
+
+__host1x_bo_unpin() drops the last reference to the mapping and frees
+it, so we can't dereference mapping afterwards. The cache itself
+outlives the mapping, so use the cache local variable instead.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/host1x/bus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
+index ef42e9a883f4f9..4dd83627fae06a 100644
+--- a/drivers/gpu/host1x/bus.c
++++ b/drivers/gpu/host1x/bus.c
+@@ -1006,10 +1006,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+               if (WARN_ON(!cache))
+                       continue;
+-              mutex_lock(&mapping->cache->lock);
++              mutex_lock(&cache->lock);
+               WARN_ON(kref_read(&mapping->ref) != 1);
+               __host1x_bo_unpin(&mapping->ref);
+-              mutex_unlock(&mapping->cache->lock);
++              mutex_unlock(&cache->lock);
+       }
+ }
+ EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
+-- 
+2.53.0
+
diff --git a/queue-6.12/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-6.12/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..09a4743
--- /dev/null
@@ -0,0 +1,114 @@
+From 92bbe6326706daf9ab63a168cb85f421ae4b223d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 4073e66e4d629e..3dc624c75bb5c5 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.12/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-6.12/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..47a1a2a
--- /dev/null
@@ -0,0 +1,42 @@
+From 3aadebb970adeb16b0bb9651a6c88578b5124ab1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 3dc624c75bb5c5..4cfcf6e945bba5 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
diff --git a/queue-6.12/revert-arm64-dts-ti-k3-am62a7-sk-add-bootph-all-tag-.patch b/queue-6.12/revert-arm64-dts-ti-k3-am62a7-sk-add-bootph-all-tag-.patch
new file mode 100644 (file)
index 0000000..4c9dfc7
--- /dev/null
@@ -0,0 +1,35 @@
+From 211d2798eb29ec6d49b876e73ffb37ab5a082900 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 21:49:33 -0400
+Subject: Revert "arm64: dts: ti: k3-am62a7-sk: Add bootph-all tag to vqmmc"
+
+This reverts commit 1b21939117aea112f6b7fa67bba6fb43d2a8e8ec.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
+index f94378379447a6..0b6e371eea98d2 100644
+--- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
++++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
+@@ -134,7 +134,6 @@ vddshv_sdio: regulator-5 {
+               gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>;
+               states = <1800000 0x0>,
+                        <3300000 0x1>;
+-              bootph-all;
+       };
+       leds {
+@@ -382,7 +381,6 @@ pmic_irq_pins_default: pmic-irq-default-pins {
+               pinctrl-single,pins = <
+                       AM62AX_MCU_IOPAD(0x000, PIN_INPUT, 7) /* (E11) MCU_GPIO0_0 */
+               >;
+-              bootph-all;
+       };
+ };
+-- 
+2.53.0
+
index c22ed8bec4c2cb2fc030093e24c06e9a51adf1e6..f8f3aa0e24a28d1813a4b7cac2e42dc2f9d73b8c 100644 (file)
@@ -15,3 +15,21 @@ kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
 kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.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
+can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
+can-isotp-serialize-tx-state-transitions-under-so-rx.patch
+dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
+revert-arm64-dts-ti-k3-am62a7-sk-add-bootph-all-tag-.patch
+gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
+crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
+xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
+soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
diff --git a/queue-6.12/soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch b/queue-6.12/soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
new file mode 100644 (file)
index 0000000..482b5a0
--- /dev/null
@@ -0,0 +1,87 @@
+From 9e64c6d584529e2672d1cfc5b8ec7a43714865c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Apr 2026 17:29:19 +0530
+Subject: soc: qcom: ice: Allow explicit votes on 'iface' clock for ICE
+
+From: Harshal Dev <harshal.dev@oss.qualcomm.com>
+
+[ Upstream commit 0d5dc5818191b55e4364d04b1b898a14a2ccac38 ]
+
+Since Qualcomm inline-crypto engine (ICE) is now a dedicated driver
+de-coupled from the QCOM UFS driver, it explicitly votes for its required
+clocks during probe. For scenarios where the 'clk_ignore_unused' flag is
+not passed on the kernel command line, to avoid potential unclocked ICE
+hardware register access during probe the ICE driver should additionally
+vote on the 'iface' clock.
+Also update the suspend and resume callbacks to handle un-voting and voting
+on the 'iface' clock.
+
+Fixes: 2afbf43a4aec6 ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver")
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Reviewed-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-2-5ccf5d7e2846@oss.qualcomm.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/soc/qcom/ice.c | 17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
+index 85504a023e326a..0aa83175fbda10 100644
+--- a/drivers/soc/qcom/ice.c
++++ b/drivers/soc/qcom/ice.c
+@@ -49,6 +49,7 @@ struct qcom_ice {
+       struct device_link *link;
+       struct clk *core_clk;
++      struct clk *iface_clk;
+ };
+ static DEFINE_XARRAY(ice_handles);
+@@ -150,8 +151,13 @@ int qcom_ice_resume(struct qcom_ice *ice)
+       err = clk_prepare_enable(ice->core_clk);
+       if (err) {
+-              dev_err(dev, "failed to enable core clock (%d)\n",
+-                      err);
++              dev_err(dev, "Failed to enable core clock: %d\n", err);
++              return err;
++      }
++
++      err = clk_prepare_enable(ice->iface_clk);
++      if (err) {
++              dev_err(dev, "Failed to enable iface clock: %d\n", err);
+               return err;
+       }
+@@ -161,6 +167,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume);
+ int qcom_ice_suspend(struct qcom_ice *ice)
+ {
++      clk_disable_unprepare(ice->iface_clk);
+       clk_disable_unprepare(ice->core_clk);
+       return 0;
+@@ -241,11 +248,17 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
+       engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk");
+       if (!engine->core_clk)
+               engine->core_clk = devm_clk_get_optional_enabled(dev, "ice");
++      if (!engine->core_clk)
++              engine->core_clk = devm_clk_get_optional_enabled(dev, "core");
+       if (!engine->core_clk)
+               engine->core_clk = devm_clk_get_enabled(dev, NULL);
+       if (IS_ERR(engine->core_clk))
+               return ERR_CAST(engine->core_clk);
++      engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface");
++      if (IS_ERR(engine->iface_clk))
++              return ERR_CAST(engine->iface_clk);
++
+       if (!qcom_ice_check_supported(engine))
+               return ERR_PTR(-EOPNOTSUPP);
+-- 
+2.53.0
+
diff --git a/queue-6.12/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch b/queue-6.12/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
new file mode 100644 (file)
index 0000000..58aa5c5
--- /dev/null
@@ -0,0 +1,110 @@
+From ade5088656cc856242934b93b6d56630edf81136 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 10:14:04 -0400
+Subject: xprtrdma: Clear receive-side ownership pointers on release
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ]
+
+Three small ownership-state cleanups land the transport in a
+state that lets future reviewers reason about each pointer
+locally rather than tracing the whole reply path:
+
+rpcrdma_rep_put() clears rep->rr_rqst before the rep enters
+rb_free_reps so that no rep on the free list still carries a
+stale rqst pointer.  rpcrdma_reply_handler() and
+rpcrdma_unpin_rqst() are the only sites that set rr_rqst;
+rpcrdma_reply_handler() hands the rep through
+rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst
+directly because its error path abandons the rep for
+teardown cleanup rather than returning it to rb_free_reps.
+
+rpcrdma_reply_put() NULLs req->rl_reply before calling
+rpcrdma_rep_put().  The previous order placed the rep on
+rb_free_reps while req->rl_reply still pointed at it; the
+window was harmless because xprt_rdma_free_slot() holds the
+req exclusively across the pair, but closing it makes the
+invariant 'rep on rb_free_reps implies no req references it'
+strictly checkable.
+
+rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear
+req->rl_sendctx after dropping the sendctx pointer in the
+sendctx ring.  Without this, req->rl_sendctx survives across
+Send completion and points at a sendctx that may already have
+been reassigned by rpcrdma_sendctx_get_locked() to a different
+req.  No caller dereferences the stale pointer today --
+rpcrdma_prepare_send_sges() overwrites it before the next
+Send -- but a NULL is a more honest representation of 'the
+Send is no longer outstanding' and lets the assertion patch
+that follows trip on any future regression.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sunrpc/xprtrdma/rpc_rdma.c |  4 ++++
+ net/sunrpc/xprtrdma/verbs.c    | 12 ++++++++++--
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
+index e201b37578a70e..aa57e057ff451f 100644
+--- a/net/sunrpc/xprtrdma/rpc_rdma.c
++++ b/net/sunrpc/xprtrdma/rpc_rdma.c
+@@ -542,6 +542,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+       rpcrdma_req_put(req);
+ }
+@@ -550,8 +551,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+  */
+ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+ {
++      struct rpcrdma_req *req = sc->sc_req;
++
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+ }
+ /* Prepare an SGE for the RPC-over-RDMA transport header.
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index efd91ed249fabe..e2d3cee825cd7f 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1080,9 +1080,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
+  * @buf: buffer pool
+  * @rep: rep to release
+  *
++ * The rep's transient association with an rpc_rqst, established
++ * by rpcrdma_reply_handler() and torn down here, must not survive
++ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
++ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
++ * would imply the rep is still referenced by a req.
+  */
+ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
+ {
++      rep->rr_rqst = NULL;
+       llist_add(&rep->rr_node, &buf->rb_free_reps);
+ }
+@@ -1265,9 +1271,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
+  */
+ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
+ {
+-      if (req->rl_reply) {
+-              rpcrdma_rep_put(buffers, req->rl_reply);
++      struct rpcrdma_rep *rep = req->rl_reply;
++
++      if (rep) {
+               req->rl_reply = NULL;
++              rpcrdma_rep_put(buffers, rep);
+       }
+       /* I2: rl_reply NULL after the put closes the
+        * 'rep on rb_free_reps still referenced by req' window.
+-- 
+2.53.0
+
diff --git a/queue-6.18/can-bcm-add-locking-when-updating-filter-and-timer-v.patch b/queue-6.18/can-bcm-add-locking-when-updating-filter-and-timer-v.patch
new file mode 100644 (file)
index 0000000..6a227c2
--- /dev/null
@@ -0,0 +1,417 @@
+From aaa7b99cc992f559597e50b241226447d5e5a5e1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:51 +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 | 176 ++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 133 insertions(+), 43 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index a40519638a262b..cfc495106aac4d 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -128,6 +128,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 {
+@@ -292,21 +293,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) {
+@@ -335,14 +342,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:
+@@ -456,7 +469,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 */
+@@ -473,7 +486,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) ?
+@@ -617,6 +630,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' */
+@@ -633,6 +648,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;
+@@ -681,15 +698,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)) {
++      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);
+-              return HRTIMER_RESTART;
++              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;
+ }
+ /*
+@@ -699,8 +727,10 @@ 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;
+       unsigned char traffic_flags;
++      bool rtr_frame;
+       if (op->can_id != rxframe->can_id)
+               return;
+@@ -724,9 +754,18 @@ 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;
+       }
+@@ -738,6 +777,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
++      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,
+@@ -773,6 +814,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);
+ }
+ /*
+@@ -1115,7 +1158,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);
+@@ -1129,6 +1172,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)
+  */
+@@ -1163,6 +1224,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 */
+               /*
+@@ -1174,19 +1237,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;
+@@ -1197,6 +1289,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);
+@@ -1238,6 +1331,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 */
+@@ -1262,29 +1357,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)
+@@ -1294,9 +1382,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
+
diff --git a/queue-6.18/can-bcm-add-missing-device-refcount-for-can-filter-r.patch b/queue-6.18/can-bcm-add-missing-device-refcount-for-can-filter-r.patch
new file mode 100644 (file)
index 0000000..92bfeef
--- /dev/null
@@ -0,0 +1,121 @@
+From 74de3f65c64ce523552a5b76cd0bbef583942a9e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:55 +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 2de64d7a99a216..ca0dd2229f5b86 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -932,6 +932,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);
+@@ -962,17 +963,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,
+@@ -1491,7 +1489,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
+@@ -1864,16 +1870,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
+
diff --git a/queue-6.18/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch b/queue-6.18/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
new file mode 100644 (file)
index 0000000..b5dbac8
--- /dev/null
@@ -0,0 +1,230 @@
+From e7f8d6dc3fc7ddbd12fbaa6327b4ad145fd33e76 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:53 +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 1d4323cdf48a47..d7f91afda3c85d 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -127,7 +127,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 */
+ };
+@@ -467,12 +467,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;
+@@ -489,25 +495,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);
+       }
+@@ -1031,6 +1059,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 */
+               /*
+@@ -1041,11 +1071,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)
+@@ -1055,36 +1097,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 */
+@@ -1160,10 +1204,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
+
diff --git a/queue-6.18/can-bcm-fix-can-frame-rx-tx-statistics.patch b/queue-6.18/can-bcm-fix-can-frame-rx-tx-statistics.patch
new file mode 100644 (file)
index 0000000..cec7329
--- /dev/null
@@ -0,0 +1,200 @@
+From d7eada5cb9a07fa7443a5cac9ea8e86697cf4744 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:52 +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 | 69 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 46 insertions(+), 23 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index cfc495106aac4d..1d4323cdf48a47 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -111,7 +111,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;
+@@ -228,10 +228,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,
+@@ -253,9 +256,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);
+@@ -279,7 +282,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');
+@@ -289,6 +293,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
+@@ -340,7 +362,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
+@@ -500,12 +522,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 &= ~RX_THR;
+@@ -751,24 +770,30 @@ 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;
+       }
++      /* 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);
++
+       /* compute flags to distinguish between own/local/remote CAN traffic */
+       traffic_flags = 0;
+       if (skb->sk) {
+@@ -777,8 +802,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
+-      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,
+-- 
+2.53.0
+
diff --git a/queue-6.18/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch b/queue-6.18/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
new file mode 100644 (file)
index 0000000..0bbfc6c
--- /dev/null
@@ -0,0 +1,74 @@
+From b6e0abd360418888050fb72725c62ca279e246c7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:57 +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 8c138376591f37..079a6a801f42e3 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -794,11 +794,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);
+@@ -830,6 +825,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
++      /* 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
+
diff --git a/queue-6.18/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch b/queue-6.18/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
new file mode 100644 (file)
index 0000000..e418314
--- /dev/null
@@ -0,0 +1,164 @@
+From 9fed2cc86e0fcdb4bff58d1a5b8f8297ba1d114d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:56 +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 ca0dd2229f5b86..8c138376591f37 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1281,6 +1281,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))) {
+@@ -1365,8 +1366,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 */
+@@ -1433,6 +1441,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))) */
+@@ -1446,7 +1455,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;
+@@ -1476,7 +1485,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;
+@@ -1506,18 +1518,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;
+@@ -1733,11 +1759,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
+
diff --git a/queue-6.18/can-bcm-track-a-single-source-interface-for-anydev-t.patch b/queue-6.18/can-bcm-track-a-single-source-interface-for-anydev-t.patch
new file mode 100644 (file)
index 0000000..f8373cf
--- /dev/null
@@ -0,0 +1,143 @@
+From c0cf15359cc3d0d3ddbc8aaaf413dfdbd784f12f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:58 +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 079a6a801f42e3..76bc7f72742535 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -116,6 +116,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;
+@@ -791,6 +792,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       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);
+@@ -825,10 +853,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       traffic_flags |= RX_OWN;
+       }
+-      /* 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;
+@@ -1363,6 +1390,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);
+@@ -1763,10 +1791,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
+
diff --git a/queue-6.18/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch b/queue-6.18/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
new file mode 100644 (file)
index 0000000..fbc2dd0
--- /dev/null
@@ -0,0 +1,128 @@
+From 658d180b11123909f7d092e5ca6e5349b9c3764c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:25:54 +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 d7f91afda3c85d..2de64d7a99a216 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1241,22 +1241,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;
+ }
+ /*
+@@ -1319,7 +1334,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);
+@@ -1392,16 +1411,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 */
+@@ -1500,6 +1515,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
+
diff --git a/queue-6.18/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch b/queue-6.18/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
new file mode 100644 (file)
index 0000000..5b5b6aa
--- /dev/null
@@ -0,0 +1,44 @@
+From c7e4826832000515d3dd46bb8db232747d54ba1e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:34:52 +0900
+Subject: crypto: tegra - Don't touch bo refcount in host1x bo pin/unpin
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit f8c9c57d750346abd213ffed2ae3cacb0268e9f1 ]
+
+Since commit "gpu: host1x: Allow entries in BO caches to be freed",
+host1x_bo_pin() and host1x_bo_unpin() handle the bo's refcount
+themselves. .pin/.unpin callbacks should not adjust it.
+
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/crypto/tegra/tegra-se-main.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c
+index 2755f19ad05e92..77b923a6b7c18c 100644
+--- a/drivers/crypto/tegra/tegra-se-main.c
++++ b/drivers/crypto/tegra/tegra-se-main.c
+@@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire
+               return ERR_PTR(-ENOMEM);
+       kref_init(&map->ref);
+-      map->bo = host1x_bo_get(bo);
++      map->bo = bo;
+       map->direction = direction;
+       map->dev = dev;
+@@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map)
+       dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
+       sg_free_table(map->sgt);
+       kfree(map->sgt);
+-      host1x_bo_put(map->bo);
+       kfree(map);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch b/queue-6.18/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
new file mode 100644 (file)
index 0000000..8e3b3a3
--- /dev/null
@@ -0,0 +1,203 @@
+From 5a500602f6dccea021f20ab5af3648547a07b43f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 14:59:09 +0200
+Subject: dmaengine: sh: rz-dmac: Move interrupt request after everything is
+ set up
+
+From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream.
+
+Once the interrupt is requested, the interrupt handler may run immediately.
+Since the IRQ handler can access channel->ch_base, which is initialized
+only after requesting the IRQ, this may lead to invalid memory access.
+Likewise, the IRQ thread may access uninitialized data (the ld_free,
+ld_queue, and ld_active lists), which may also lead to issues.
+
+Request the interrupts only after everything is set up. To keep the error
+path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent().
+
+Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
+Cc: stable@vger.kernel.org
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Tested-by: John Madieu <john.madieu.xa@bp.renesas.com>
+Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+[tm: Kept the channel->irq field in rz_dmac_chan_probe() instead of
+ upstream's local `irq` variable, as commit 04e227718ab8
+ ("dmaengine: sh: rz-dmac: Make channel irq local") is not present
+ in this tree. Likewise kept platform_get_irq_byname() instead of
+ platform_get_irq_byname_optional() for the error IRQ in rz_dmac_probe(),
+ as commit 6b3a6b6dc074 ("dmaengine: sh: rz_dmac: make error interrupt
+ optional") is not present in this tree either; its early return on
+ failure becomes a goto err jump to match the new call order.]
+Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sh/rz-dmac.c | 96 ++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 58 deletions(-)
+
+diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
+index 818d1ef6f0bf94..4db84b83177a6d 100644
+--- a/drivers/dma/sh/rz-dmac.c
++++ b/drivers/dma/sh/rz-dmac.c
+@@ -811,27 +811,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       channel->index = index;
+       channel->mid_rid = -EINVAL;
+-      /* Request the channel interrupt. */
+-      scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
+-      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
+-      if (channel->irq < 0)
+-              return channel->irq;
+-
+-      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
+-                               dev_name(dmac->dev), index);
+-      if (!irqname)
+-              return -ENOMEM;
+-
+-      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
+-                                      rz_dmac_irq_handler,
+-                                      rz_dmac_irq_handler_thread, 0,
+-                                      irqname, channel);
+-      if (ret) {
+-              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
+-                      channel->irq, ret);
+-              return ret;
+-      }
+-
+       /* Set io base address for each channel */
+       if (index < 8) {
+               channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET +
+@@ -844,9 +823,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       }
+       /* Allocate descriptors */
+-      lmdesc = dma_alloc_coherent(&pdev->dev,
+-                                  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                  &channel->lmdesc.base_dma, GFP_KERNEL);
++      lmdesc = dmam_alloc_coherent(&pdev->dev,
++                                   sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
++                                   &channel->lmdesc.base_dma, GFP_KERNEL);
+       if (!lmdesc) {
+               dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n");
+               return -ENOMEM;
+@@ -862,7 +841,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       INIT_LIST_HEAD(&channel->ld_free);
+       INIT_LIST_HEAD(&channel->ld_active);
+-      return 0;
++      /* Request the channel interrupt. */
++      scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
++      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
++      if (channel->irq < 0)
++              return channel->irq;
++
++      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
++                               dev_name(dmac->dev), index);
++      if (!irqname)
++              return -ENOMEM;
++
++      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
++                                      rz_dmac_irq_handler,
++                                      rz_dmac_irq_handler_thread, 0,
++                                      irqname, channel);
++      if (ret)
++              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
++                      channel->irq, ret);
++
++      return ret;
+ }
+ static void rz_dmac_put_device(void *_dev)
+@@ -932,7 +930,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       const char *irqname = "error";
+       struct dma_device *engine;
+       struct rz_dmac *dmac;
+-      int channel_num;
+       int ret;
+       int irq;
+       u8 i;
+@@ -964,19 +961,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+                       return PTR_ERR(dmac->ext_base);
+       }
+-      /* Register interrupt handler for error */
+-      irq = platform_get_irq_byname(pdev, irqname);
+-      if (irq < 0)
+-              return irq;
+-
+-      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
+-                             irqname, NULL);
+-      if (ret) {
+-              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
+-                      irq, ret);
+-              return ret;
+-      }
+-
+       /* Initialize the channels. */
+       INIT_LIST_HEAD(&dmac->engine.channels);
+@@ -1002,6 +986,21 @@ static int rz_dmac_probe(struct platform_device *pdev)
+                       goto err;
+       }
++      /* Register interrupt handler for error */
++      irq = platform_get_irq_byname(pdev, irqname);
++      if (irq < 0) {
++              ret = irq;
++              goto err;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
++                             irqname, NULL);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
++                      irq, ret);
++              goto err;
++      }
++
+       /* Register the DMAC as a DMA provider for DT. */
+       ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate,
+                                        NULL);
+@@ -1040,16 +1039,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ dma_register_err:
+       of_dma_controller_free(pdev->dev.of_node);
+ err:
+-      channel_num = i ? i - 1 : 0;
+-      for (i = 0; i < channel_num; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+-
+       reset_control_assert(dmac->rstc);
+ err_pm_runtime_put:
+       pm_runtime_put(&pdev->dev);
+@@ -1062,18 +1051,9 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ static void rz_dmac_remove(struct platform_device *pdev)
+ {
+       struct rz_dmac *dmac = platform_get_drvdata(pdev);
+-      unsigned int i;
+       dma_async_device_unregister(&dmac->engine);
+       of_dma_controller_free(pdev->dev.of_node);
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+       reset_control_assert(dmac->rstc);
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+-- 
+2.53.0
+
diff --git a/queue-6.18/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch b/queue-6.18/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
new file mode 100644 (file)
index 0000000..e6f99da
--- /dev/null
@@ -0,0 +1,43 @@
+From 9e2970db6498c7f9a496b03dda81d97eb94bdb8a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jun 2026 17:37:49 +0900
+Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ]
+
+__host1x_bo_unpin() drops the last reference to the mapping and frees
+it, so we can't dereference mapping afterwards. The cache itself
+outlives the mapping, so use the cache local variable instead.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/host1x/bus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
+index 3801eab63282ff..3313ead8bfcc24 100644
+--- a/drivers/gpu/host1x/bus.c
++++ b/drivers/gpu/host1x/bus.c
+@@ -1001,10 +1001,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+               if (WARN_ON(!cache))
+                       continue;
+-              mutex_lock(&mapping->cache->lock);
++              mutex_lock(&cache->lock);
+               WARN_ON(kref_read(&mapping->ref) != 1);
+               __host1x_bo_unpin(&mapping->ref);
+-              mutex_unlock(&mapping->cache->lock);
++              mutex_unlock(&cache->lock);
+       }
+ }
+ EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
+-- 
+2.53.0
+
diff --git a/queue-6.18/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-6.18/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..325b19b
--- /dev/null
@@ -0,0 +1,114 @@
+From 2792fa78a13d2f31389f6ad1bf637a689cdfd152 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 94113e14bf5995..7c837094c498ed 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.18/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-6.18/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..8068e8e
--- /dev/null
@@ -0,0 +1,42 @@
+From 28445c99366bbb5e397b081458910faae79be3fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 7c837094c498ed..168662a2cc890c 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
index 6141a21c431f3d8bec89c08b9052883c8434d204..da7a5c9889e7f3b5acb8e93446b4cced1d0dddca 100644 (file)
@@ -15,3 +15,17 @@ kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
 kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.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
+dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
+gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
+crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
+xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
diff --git a/queue-6.18/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch b/queue-6.18/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
new file mode 100644 (file)
index 0000000..ab135ab
--- /dev/null
@@ -0,0 +1,110 @@
+From fe6260c94808f61ccc7c38b03514cb95178a12b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 10:14:04 -0400
+Subject: xprtrdma: Clear receive-side ownership pointers on release
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ]
+
+Three small ownership-state cleanups land the transport in a
+state that lets future reviewers reason about each pointer
+locally rather than tracing the whole reply path:
+
+rpcrdma_rep_put() clears rep->rr_rqst before the rep enters
+rb_free_reps so that no rep on the free list still carries a
+stale rqst pointer.  rpcrdma_reply_handler() and
+rpcrdma_unpin_rqst() are the only sites that set rr_rqst;
+rpcrdma_reply_handler() hands the rep through
+rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst
+directly because its error path abandons the rep for
+teardown cleanup rather than returning it to rb_free_reps.
+
+rpcrdma_reply_put() NULLs req->rl_reply before calling
+rpcrdma_rep_put().  The previous order placed the rep on
+rb_free_reps while req->rl_reply still pointed at it; the
+window was harmless because xprt_rdma_free_slot() holds the
+req exclusively across the pair, but closing it makes the
+invariant 'rep on rb_free_reps implies no req references it'
+strictly checkable.
+
+rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear
+req->rl_sendctx after dropping the sendctx pointer in the
+sendctx ring.  Without this, req->rl_sendctx survives across
+Send completion and points at a sendctx that may already have
+been reassigned by rpcrdma_sendctx_get_locked() to a different
+req.  No caller dereferences the stale pointer today --
+rpcrdma_prepare_send_sges() overwrites it before the next
+Send -- but a NULL is a more honest representation of 'the
+Send is no longer outstanding' and lets the assertion patch
+that follows trip on any future regression.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sunrpc/xprtrdma/rpc_rdma.c |  4 ++++
+ net/sunrpc/xprtrdma/verbs.c    | 12 ++++++++++--
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
+index 5d1f96bffd7491..e2694fb03ba1b3 100644
+--- a/net/sunrpc/xprtrdma/rpc_rdma.c
++++ b/net/sunrpc/xprtrdma/rpc_rdma.c
+@@ -542,6 +542,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+       rpcrdma_req_put(req);
+ }
+@@ -550,8 +551,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+  */
+ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+ {
++      struct rpcrdma_req *req = sc->sc_req;
++
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+ }
+ /* Prepare an SGE for the RPC-over-RDMA transport header.
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index efd91ed249fabe..e2d3cee825cd7f 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1080,9 +1080,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
+  * @buf: buffer pool
+  * @rep: rep to release
+  *
++ * The rep's transient association with an rpc_rqst, established
++ * by rpcrdma_reply_handler() and torn down here, must not survive
++ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
++ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
++ * would imply the rep is still referenced by a req.
+  */
+ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
+ {
++      rep->rr_rqst = NULL;
+       llist_add(&rep->rr_node, &buf->rb_free_reps);
+ }
+@@ -1265,9 +1271,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
+  */
+ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
+ {
+-      if (req->rl_reply) {
+-              rpcrdma_rep_put(buffers, req->rl_reply);
++      struct rpcrdma_rep *rep = req->rl_reply;
++
++      if (rep) {
+               req->rl_reply = NULL;
++              rpcrdma_rep_put(buffers, rep);
+       }
+       /* I2: rl_reply NULL after the put closes the
+        * 'rep on rb_free_reps still referenced by req' window.
+-- 
+2.53.0
+
diff --git a/queue-6.6/accel-ivpu-reject-firmware-log-with-size-smaller-tha.patch b/queue-6.6/accel-ivpu-reject-firmware-log-with-size-smaller-tha.patch
new file mode 100644 (file)
index 0000000..f4ee537
--- /dev/null
@@ -0,0 +1,56 @@
+From 050f0e14b594c7cf2bf56f500fbe6199b3ecd0c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 11:23:40 +0530
+Subject: accel/ivpu: Reject firmware log with size smaller than header
+
+From: Jhonraushan <raushan.jhon@gmail.com>
+
+commit ddb44baed257560f192b145ed36cf8c0a412de47 upstream.
+
+fw_log_from_bo() validates the tracing buffer header_size and that the
+log fits within the BO, but never checks that log->size is at least
+log->header_size. fw_log_print_buffer() then computes:
+
+  u32 data_size = log->size - log->header_size;
+
+which underflows to a near-U32_MAX value when firmware reports a log whose
+size is smaller than its header. That huge data_size defeats the
+log_start/log_end bounds clamps added by commit dd1311bcf0e6 ("accel/ivpu:
+Add bounds checks for firmware log indices"), so fw_log_print_lines() reads
+far past the small real data region of the BO. A size of 0 also makes
+fw_log_from_bo() advance the offset by 0, causing the callers to loop
+forever on the same header.
+
+Reject logs whose size is smaller than the header (which also rejects
+size == 0).
+
+Fixes: d4e4257afa6e ("accel/ivpu: Add firmware tracing support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jhonraushan <raushan.jhon@gmail.com>
+Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Signed-off-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Link: https://patch.msgid.link/20260715074206.867712-1-raushan.jhon@gmail.com
+Signed-off-by: Raushan Patel <raushan.jhon@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accel/ivpu/ivpu_fw_log.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c
+index 95065cac9fbdc4..ccad9aa99e5d64 100644
+--- a/drivers/accel/ivpu/ivpu_fw_log.c
++++ b/drivers/accel/ivpu/ivpu_fw_log.c
+@@ -43,6 +43,10 @@ static int fw_log_ptr(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *offset,
+               ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
+               return -EINVAL;
+       }
++      if (log->size < log->header_size) {
++              ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
++              return -EINVAL;
++      }
+       if ((char *)log + log->size > (char *)bo->kvaddr + bo->base.size) {
+               ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
+               return -EINVAL;
+-- 
+2.53.0
+
diff --git a/queue-6.6/can-bcm-add-locking-when-updating-filter-and-timer-v.patch b/queue-6.6/can-bcm-add-locking-when-updating-filter-and-timer-v.patch
new file mode 100644 (file)
index 0000000..6a333ce
--- /dev/null
@@ -0,0 +1,412 @@
+From 1a4cb9529429962db2c6a510798c3e0b40c8cca8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:03 +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 | 176 ++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 133 insertions(+), 43 deletions(-)
+
+diff --git a/net/can/bcm.c b/net/can/bcm.c
+index 04653df3a173e2..a80a935d6b5009 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)) {
++      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);
+-              return HRTIMER_RESTART;
++              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;
+@@ -691,12 +721,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);
+@@ -730,6 +771,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);
+ }
+ /*
+@@ -1073,7 +1116,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);
+@@ -1087,6 +1130,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)
+  */
+@@ -1121,6 +1182,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 */
+               /*
+@@ -1132,19 +1195,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;
+@@ -1155,6 +1247,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);
+@@ -1196,6 +1289,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 */
+@@ -1223,29 +1318,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)
+@@ -1255,9 +1343,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
+
diff --git a/queue-6.6/can-bcm-add-missing-device-refcount-for-can-filter-r.patch b/queue-6.6/can-bcm-add-missing-device-refcount-for-can-filter-r.patch
new file mode 100644 (file)
index 0000000..938a96d
--- /dev/null
@@ -0,0 +1,121 @@
+From 7744ba91369f4514c97b5f74e7c25878df5f7aa4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:07 +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 f7733e61690613..b37e494de256b6 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -889,6 +889,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);
+@@ -919,17 +920,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,
+@@ -1452,7 +1450,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
+@@ -1825,16 +1831,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
+
diff --git a/queue-6.6/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch b/queue-6.6/can-bcm-extend-bcm_tx_lock-usage-for-data-and-timer-.patch
new file mode 100644 (file)
index 0000000..fa206cf
--- /dev/null
@@ -0,0 +1,230 @@
+From 6256dfe38232939dc9af3e43872b460579c434a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:05 +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 980d61d853ee96..ab94caa2d006b0 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);
+       }
+@@ -988,6 +1016,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 */
+               /*
+@@ -998,11 +1028,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)
+@@ -1012,36 +1054,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 */
+@@ -1118,10 +1162,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
+
diff --git a/queue-6.6/can-bcm-fix-can-frame-rx-tx-statistics.patch b/queue-6.6/can-bcm-fix-can-frame-rx-tx-statistics.patch
new file mode 100644 (file)
index 0000000..710833b
--- /dev/null
@@ -0,0 +1,191 @@
+From c250391e31d2d7ed7da81326c63ac9b9e9ab5757 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:04 +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 a80a935d6b5009..980d61d853ee96 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);
+@@ -718,25 +737,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
+
diff --git a/queue-6.6/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch b/queue-6.6/can-bcm-fix-data-race-on-rx_stamp-rx_ifindex-in-bcm_.patch
new file mode 100644 (file)
index 0000000..caed33c
--- /dev/null
@@ -0,0 +1,74 @@
+From 46eded3636da18fe429e2da6541e42b677c0c653 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:09 +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 80d065b5ebe467..0869630a0dc06b 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -761,11 +761,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);
+@@ -789,6 +784,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
+
diff --git a/queue-6.6/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch b/queue-6.6/can-bcm-fix-stale-rx-tx-ops-after-device-removal.patch
new file mode 100644 (file)
index 0000000..9234b18
--- /dev/null
@@ -0,0 +1,164 @@
+From 042f470eec4e550871283bdd29620ea92ef1450e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:08 +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 b37e494de256b6..80d065b5ebe467 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1239,6 +1239,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))) {
+@@ -1323,8 +1324,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 */
+@@ -1394,6 +1402,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))) */
+@@ -1407,7 +1416,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;
+@@ -1437,7 +1446,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;
+@@ -1467,18 +1479,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;
+@@ -1694,11 +1720,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
+
diff --git a/queue-6.6/can-bcm-track-a-single-source-interface-for-anydev-t.patch b/queue-6.6/can-bcm-track-a-single-source-interface-for-anydev-t.patch
new file mode 100644 (file)
index 0000000..9dc8d5f
--- /dev/null
@@ -0,0 +1,143 @@
+From c267bd5d307ef2b926f31f48f3656840ad916b8f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:10 +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 0869630a0dc06b..fcd923c223f4ba 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;
+@@ -758,6 +759,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
+                       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);
+@@ -784,10 +812,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;
+@@ -1321,6 +1348,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);
+@@ -1724,10 +1752,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
+
diff --git a/queue-6.6/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch b/queue-6.6/can-bcm-validate-frame-length-in-bcm_rx_setup-for-rt.patch
new file mode 100644 (file)
index 0000000..563f88f
--- /dev/null
@@ -0,0 +1,128 @@
+From 7886e5087b981f01f85e5033bcb8cecea03fc9f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:24:06 +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 ab94caa2d006b0..f7733e61690613 100644
+--- a/net/can/bcm.c
++++ b/net/can/bcm.c
+@@ -1199,22 +1199,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;
+ }
+ /*
+@@ -1277,7 +1292,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);
+@@ -1350,16 +1369,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 */
+@@ -1461,6 +1476,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
+
diff --git a/queue-6.6/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch b/queue-6.6/can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
new file mode 100644 (file)
index 0000000..4a10510
--- /dev/null
@@ -0,0 +1,238 @@
+From 29d63b803007ca0a6595b342ceb5b87683e67e57 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:34:08 +0200
+Subject: can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream.
+
+isotp_release() looked up the bound network device via dev_get_by_index()
+using the stored ifindex. During device unregistration the device is
+unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier
+chain runs, so a concurrent isotp_release() could find no device, skip
+can_rx_unregister() entirely, and still proceed to free the socket.
+Since isotp_release() had already removed itself from the isotp
+notifier list at that point, isotp_notify() would never get a chance to
+clean up either, leaving a stale CAN filter that keeps pointing at the
+freed socket.
+
+Fix this the same way raw.c already does: hold a tracked reference to
+the bound net_device in the socket (so->dev/so->dev_tracker) from
+bind() onward instead of re-resolving it from the ifindex, and
+serialize bind()/release() with rtnl_lock() so that so->dev is always
+consistent with what the NETDEV_UNREGISTER notifier sees. so->dev
+stays valid regardless of ifindex-hash unlisting, and is only ever
+cleared by whichever of isotp_release()/isotp_notify() gets there
+first, so the filter is always removed exactly once.
+
+isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state
+isn't ISOTP_IDLE yet, so a timer left running by a prior
+NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks
+share the same lock_sock() section, so there is no window in which a
+concurrent isotp_notify() clearing so->bound could be missed.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@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/isotp.c | 88 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 59 insertions(+), 29 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 80adf7366e63a8..b906fcdb386cd4 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -150,6 +150,7 @@ struct isotp_sock {
+       struct sock sk;
+       int bound;
+       int ifindex;
++      struct net_device *dev;
+       canid_t txid;
+       canid_t rxid;
+       ktime_t tx_gap;
+@@ -962,6 +963,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       goto err_event_drop;
+       }
++      /* so->bound is only checked once above - a wakeup may have
++       * unbound/rebound the socket meanwhile, so re-validate it
++       */
++      if (!so->bound) {
++              err = -EADDRNOTAVAIL;
++              goto err_out_drop;
++      }
++
+       /* PDU size > default => try max_pdu_size */
+       if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) {
+               u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL);
+@@ -1197,28 +1206,30 @@ static int isotp_release(struct socket *sock)
+       list_del(&so->notifier);
+       spin_unlock(&isotp_notifier_lock);
++      rtnl_lock();
+       lock_sock(sk);
+-      /* remove current filters & unregister */
+-      if (so->bound) {
+-              if (so->ifindex) {
+-                      struct net_device *dev;
+-
+-                      dev = dev_get_by_index(net, so->ifindex);
+-                      if (dev) {
+-                              if (isotp_register_rxid(so))
+-                                      can_rx_unregister(net, dev, so->rxid,
+-                                                        SINGLE_MASK(so->rxid),
+-                                                        isotp_rcv, sk);
+-
+-                              can_rx_unregister(net, dev, so->txid,
+-                                                SINGLE_MASK(so->txid),
+-                                                isotp_rcv_echo, sk);
+-                              dev_put(dev);
+-                      }
+-              }
++      /* remove current filters & unregister
++       * tracked reference so->dev is taken at bind() time with rtnl_lock
++       */
++      if (so->bound && so->dev) {
++              if (isotp_register_rxid(so))
++                      can_rx_unregister(net, so->dev, so->rxid,
++                                        SINGLE_MASK(so->rxid),
++                                        isotp_rcv, sk);
++
++              can_rx_unregister(net, so->dev, so->txid,
++                                SINGLE_MASK(so->txid),
++                                isotp_rcv_echo, sk);
++              dev_put(so->dev);
+       }
++      so->ifindex = 0;
++      so->bound = 0;
++      so->dev = NULL;
++
++      rtnl_unlock();
++
+       /* Always wait for a grace period before touching the timers below.
+        * A concurrent NETDEV_UNREGISTER may have already unregistered our
+        * filters and cleared so->bound in isotp_notify() without waiting
+@@ -1231,9 +1242,6 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txtimer);
+       hrtimer_cancel(&so->rxtimer);
+-      so->ifindex = 0;
+-      so->bound = 0;
+-
+       sock_orphan(sk);
+       sock->sk = NULL;
+@@ -1287,6 +1295,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       if (!addr->can_ifindex)
+               return -ENODEV;
++      rtnl_lock();
+       lock_sock(sk);
+       if (so->bound) {
+@@ -1294,6 +1303,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
++      /* A transmission or reception that outlived a previous binding
++       * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
++       * and RX watchdog timers bound how long this takes. Checked together
++       * with so->bound in the same lock_sock() section above, so there is
++       * no window in which a concurrent isotp_notify() could be missed.
++       */
++      if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
++              err = -EAGAIN;
++              goto out;
++      }
++
+       /* ensure different CAN IDs when the rx_id is to be registered */
+       if (isotp_register_rxid(so) && rx_id == tx_id) {
+               err = -EADDRNOTAVAIL;
+@@ -1306,14 +1326,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+               goto out;
+       }
+       if (dev->type != ARPHRD_CAN) {
+-              dev_put(dev);
+               err = -ENODEV;
+-              goto out;
++              goto out_put_dev;
+       }
+-      if (dev->mtu < so->ll.mtu) {
+-              dev_put(dev);
++      if (READ_ONCE(dev->mtu) < so->ll.mtu) {
+               err = -EINVAL;
+-              goto out;
++              goto out_put_dev;
+       }
+       if (!(dev->flags & IFF_UP))
+               notify_enetdown = 1;
+@@ -1331,16 +1349,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+       can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
+                       isotp_rcv_echo, sk, "isotpe", sk);
+-      dev_put(dev);
+-
+       /* switch to new settings */
+       so->ifindex = ifindex;
+       so->rxid = rx_id;
+       so->txid = tx_id;
+       so->bound = 1;
++      /* bind() ok -> hold a reference for so->dev so that isotp_release()
++       * can safely reach the device later, even if a concurrent
++       * NETDEV_UNREGISTER has already unlisted it by ifindex.
++       */
++      so->dev = dev;
++      dev_hold(so->dev);
++
++out_put_dev:
++      /* remove potential reference from dev_get_by_index() */
++      dev_put(dev);
+ out:
+       release_sock(sk);
++      rtnl_unlock();
+       if (notify_enetdown) {
+               sk->sk_err = ENETDOWN;
+@@ -1543,7 +1570,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return;
+-      if (so->ifindex != dev->ifindex)
++      if (so->dev != dev)
+               return;
+       switch (msg) {
+@@ -1559,10 +1586,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
+                       can_rx_unregister(dev_net(dev), dev, so->txid,
+                                         SINGLE_MASK(so->txid),
+                                         isotp_rcv_echo, sk);
++                      dev_put(so->dev);
+               }
+               so->ifindex = 0;
+               so->bound  = 0;
++              so->dev = NULL;
+               release_sock(sk);
+               sk->sk_err = ENODEV;
+@@ -1622,6 +1651,7 @@ static int isotp_init(struct sock *sk)
+       so->ifindex = 0;
+       so->bound = 0;
++      so->dev = NULL;
+       so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+       so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+-- 
+2.53.0
+
diff --git a/queue-6.6/can-isotp-serialize-tx-state-transitions-under-so-rx.patch b/queue-6.6/can-isotp-serialize-tx-state-transitions-under-so-rx.patch
new file mode 100644 (file)
index 0000000..cefbf76
--- /dev/null
@@ -0,0 +1,426 @@
+From 9fe755b867c64514a728d96242af2ffd28d59737 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:34:09 +0200
+Subject: can: isotp: serialize TX state transitions under so->rx_lock
+
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+
+commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream.
+
+The TX state machine (so->tx.state) is driven from three contexts:
+sendmsg() claiming and progressing a transfer, the RX path consuming
+Flow Control/echo frames, and two hrtimers timing out a stalled
+transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with
+hrtimer_cancel() calls made under so->rx_lock elsewhere left windows
+where a frame or timer callback could act on a state that had already
+moved on, corrupting an unrelated transfer.
+
+so->rx_lock now covers the full lifecycle of a TX claim: sendmsg()
+takes it to check so->tx.state is ISOTP_IDLE, switch it to
+ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's
+timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf()
+already run under this lock via isotp_rcv(), and isotp_rcv_echo() now
+takes it itself, so none of them can ever observe a transfer mid-claim.
+This also means a transfer can no longer be handed to sendmsg()'s
+cleanup paths (signal or send error) while another thread is
+concurrently claiming or finishing it, so those paths can cancel
+timers and reset the state unconditionally.
+
+isotp_release() claims the socket the same way, so a racing sendmsg()
+sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending.
+
+Only the hrtimer callbacks stay outside so->rx_lock, since they run
+under so->rx_lock's cancellation elsewhere and taking it themselves
+would deadlock. so->tx_gen lets them recognize whether the transfer
+they timed out is still the one currently active, so they don't
+report an error against a transfer that has since completed or been
+superseded.
+
+Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@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/isotp.c | 192 ++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 154 insertions(+), 38 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index b906fcdb386cd4..efc5eeac7c8861 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -155,7 +155,7 @@ struct isotp_sock {
+       canid_t rxid;
+       ktime_t tx_gap;
+       ktime_t lastrxcf_tstamp;
+-      struct hrtimer rxtimer, txtimer, txfrtimer;
++      struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
+       struct can_isotp_options opt;
+       struct can_isotp_fc_options rxfc, txfc;
+       struct can_isotp_ll_options ll;
+@@ -163,6 +163,7 @@ struct isotp_sock {
+       u32 force_tx_stmin;
+       u32 force_rx_stmin;
+       u32 cfecho; /* consecutive frame echo tag */
++      u32 tx_gen; /* generation, bumped per new tx transfer */
+       struct tpcon rx, tx;
+       struct list_head notifier;
+       wait_queue_head_t wait;
+@@ -369,6 +370,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+       hrtimer_cancel(&so->txtimer);
++      /* isotp_tx_timeout() may have given up on this job while
++       * hrtimer_cancel() above waited for it to finish; so->rx_lock
++       * (held by our caller isotp_rcv()) rules out a concurrent claim,
++       * so a plain recheck is enough here.
++       */
++      if (so->tx.state != ISOTP_WAIT_FC &&
++          so->tx.state != ISOTP_WAIT_FIRST_FC)
++              return 1;
++
+       if ((cf->len < ae + FC_CONTENT_SZ) ||
+           ((so->opt.flags & ISOTP_CHECK_PADDING) &&
+            check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
+@@ -414,7 +424,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
+               so->tx.bs = 0;
+               so->tx.state = ISOTP_SENDING;
+               /* send CF frame and enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+               break;
+@@ -567,6 +577,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
+       hrtimer_cancel(&so->rxtimer);
++      /* isotp_rx_timer_handler() may have raced us for so->rx.state
++       * while hrtimer_cancel() above waited for it to finish, already
++       * reporting ETIMEDOUT and resetting the reception; don't process
++       * this CF into a reassembly that has already been given up on.
++       */
++      if (so->rx.state != ISOTP_WAIT_DATA)
++              return 1;
++
+       /* CFs are never longer than the FF */
+       if (cf->len > so->rx.ll_dl)
+               return 1;
+@@ -856,20 +874,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       /* only handle my own local echo CF/SF skb's (no FF!) */
+-      if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
++      if (skb->sk != sk)
+               return;
++      /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
++       * (no isotp_rcv() caller here), so take it ourselves
++       */
++      spin_lock(&so->rx_lock);
++
++      /* so->cfecho may since belong to a new transfer; recheck under lock */
++      if (so->cfecho != *(u32 *)cf->data)
++              goto out_unlock;
++
+       /* cancel local echo timeout */
+-      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       /* local echo skb with consecutive frame has been consumed */
+       so->cfecho = 0;
++      /* claiming a transfer also takes so->rx_lock, so a plain recheck
++       * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
++       * a new claim while we're still in here
++       */
++      if (so->tx.state != ISOTP_SENDING)
++              goto out_unlock;
++
+       if (so->tx.idx >= so->tx.len) {
+               /* we are done */
+               so->tx.state = ISOTP_IDLE;
+               wake_up_interruptible(&so->wait);
+-              return;
++              goto out_unlock;
+       }
+       if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+@@ -877,53 +911,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
+               so->tx.state = ISOTP_WAIT_FC;
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+-              return;
++              goto out_unlock;
+       }
+       /* no gap between data frames needed => use burst mode */
+       if (!so->tx_gap) {
+               /* enable echo timeout handling */
+-              hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++              hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
+-              return;
++              goto out_unlock;
+       }
+       /* start timer to send next consecutive frame with correct delay */
+       hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
++
++out_unlock:
++      spin_unlock(&so->rx_lock);
+ }
+-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
++ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
++ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
++ * a new transfer with an error from the one that just timed out.
++ */
++static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
+ {
+-      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+-                                           txtimer);
+       struct sock *sk = &so->sk;
++      u32 gen = READ_ONCE(so->tx_gen);
++      u32 old_state = READ_ONCE(so->tx.state);
+       /* don't handle timeouts in IDLE or SHUTDOWN state */
+-      if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
++      if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
++              return HRTIMER_NORESTART;
++
++      /* only claim the timeout if the state is still unchanged */
++      if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
+               return HRTIMER_NORESTART;
+       /* we did not get any flow control or echo frame in time */
+-      /* report 'communication error on send' */
+-      sk->sk_err = ECOMM;
+-      if (!sock_flag(sk, SOCK_DEAD))
+-              sk_error_report(sk);
++      if (READ_ONCE(so->tx_gen) == gen) {
++              /* report 'communication error on send' */
++              sk->sk_err = ECOMM;
++              if (!sock_flag(sk, SOCK_DEAD))
++                      sk_error_report(sk);
++      }
+-      /* reset tx state */
+-      so->tx.state = ISOTP_IDLE;
+       wake_up_interruptible(&so->wait);
+       return HRTIMER_NORESTART;
+ }
++/* so->txtimer: fires when a Flow Control frame does not arrive in time */
++static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           txtimer);
++
++      return isotp_tx_timeout(so);
++}
++
++/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
++static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
++{
++      struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
++                                           echotimer);
++
++      return isotp_tx_timeout(so);
++}
++
+ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+ {
+       struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+                                            txfrtimer);
+       /* start echo timeout handling and cover below protocol error */
+-      hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
++      hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
+       /* cfecho should be consumed by isotp_rcv_echo() here */
+@@ -943,13 +1007,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+       int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
++      struct hrtimer *tx_hrt = &so->echotimer;
++      u32 new_state = ISOTP_SENDING;
+       int off;
+       int err;
+       if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+               return -EADDRNOTAVAIL;
+-      while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
++      /* claim the socket under so->rx_lock: this serializes the claim
++       * with the RX path and with sendmsg()'s own error paths below, so
++       * none of them can ever see a transfer mid-claim
++       */
++      for (;;) {
++              spin_lock_bh(&so->rx_lock);
++              if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
++                      break;
++              spin_unlock_bh(&so->rx_lock);
++
+               /* we do not support multiple buffers - for now */
+               if (msg->msg_flags & MSG_DONTWAIT)
+                       return -EAGAIN;
+@@ -958,11 +1033,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       return -EADDRNOTAVAIL;
+               /* wait for complete transmission of current pdu */
+-              err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
++              err = wait_event_interruptible(so->wait,
++                                             so->tx.state == ISOTP_IDLE);
+               if (err)
+-                      goto err_event_drop;
++                      return err;
+       }
++      /* new transfer: bump so->tx_gen and drain the old one's timers,
++       * still under the so->rx_lock we just claimed the socket with
++       */
++      WRITE_ONCE(so->tx.state, ISOTP_SENDING);
++      WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
++      hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
++      hrtimer_cancel(&so->txfrtimer);
++      so->cfecho = 0;
++      spin_unlock_bh(&so->rx_lock);
++
+       /* so->bound is only checked once above - a wakeup may have
+        * unbound/rebound the socket meanwhile, so re-validate it
+        */
+@@ -1073,18 +1160,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+                       so->cfecho = *(u32 *)cf->data;
+               } else {
+                       /* standard flow control check */
+-                      so->tx.state = ISOTP_WAIT_FIRST_FC;
++                      new_state = ISOTP_WAIT_FIRST_FC;
+                       /* start timeout for FC */
+                       hrtimer_sec = ISOTP_FC_TIMEOUT;
++                      tx_hrt = &so->txtimer;
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
+       }
+-      hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
++      spin_lock_bh(&so->rx_lock);
++      if (so->tx.state == ISOTP_SHUTDOWN) {
++              /* isotp_release() has since taken over and already drained
++               * our timers - don't send into a socket that's going away
++               */
++              spin_unlock_bh(&so->rx_lock);
++              kfree_skb(skb);
++              dev_put(dev);
++              wake_up_interruptible(&so->wait);
++              return -EADDRNOTAVAIL;
++      }
++      /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
++      so->tx.state = new_state;
++      hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
++      spin_unlock_bh(&so->rx_lock);
+       /* send the first or only CAN frame */
+       cf->flags = so->ll.tx_flags;
+@@ -1097,13 +1199,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+               pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
+                              __func__, ERR_PTR(err));
++              spin_lock_bh(&so->rx_lock);
+               /* no transmission -> no timeout monitoring */
+-              hrtimer_cancel(&so->txtimer);
+-
+-              /* reset consecutive frame echo tag */
+-              so->cfecho = 0;
+-
+-              goto err_out_drop;
++              hrtimer_cancel(tx_hrt);
++              goto err_out_drop_locked;
+       }
+       if (wait_tx_done) {
+@@ -1119,14 +1218,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+       return size;
++err_out_drop:
++      /* claimed but nothing sent yet - no timer to cancel */
++      spin_lock_bh(&so->rx_lock);
++      goto err_out_drop_locked;
+ err_event_drop:
+-      /* got signal: force tx state machine to be idle */
+-      so->tx.state = ISOTP_IDLE;
++      /* interrupted waiting on our own transfer - drain its timers */
++      spin_lock_bh(&so->rx_lock);
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
+-err_out_drop:
+-      /* drop this PDU and unlock a potential wait queue */
++      hrtimer_cancel(&so->echotimer);
++err_out_drop_locked:
++      /* release the claim; so->rx_lock still held from above */
++      so->cfecho = 0;
+       so->tx.state = ISOTP_IDLE;
++      spin_unlock_bh(&so->rx_lock);
+       wake_up_interruptible(&so->wait);
+       return err;
+@@ -1188,13 +1294,20 @@ static int isotp_release(struct socket *sock)
+       so = isotp_sk(sk);
+       net = sock_net(sk);
+-      /* wait for complete transmission of current pdu */
+-      while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
+-             cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
++      /* best-effort: wait for a running pdu to finish, but don't block on
++       * it forever - give up after the first signal
++       */
++      while (so->tx.state != ISOTP_IDLE &&
++             wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+               ;
+-      /* force state machines to be idle also when a signal occurred */
++      /* claim the socket under so->rx_lock like sendmsg() does, so its
++       * claim can't race the forced ISOTP_SHUTDOWN below; force it
++       * unconditionally, even when a signal cut the wait above short
++       */
++      spin_lock_bh(&so->rx_lock);
+       so->tx.state = ISOTP_SHUTDOWN;
++      spin_unlock_bh(&so->rx_lock);
+       so->rx.state = ISOTP_IDLE;
+       spin_lock(&isotp_notifier_lock);
+@@ -1240,6 +1353,7 @@ static int isotp_release(struct socket *sock)
+       hrtimer_cancel(&so->txfrtimer);
+       hrtimer_cancel(&so->txtimer);
++      hrtimer_cancel(&so->echotimer);
+       hrtimer_cancel(&so->rxtimer);
+       sock_orphan(sk);
+@@ -1682,6 +1796,8 @@ static int isotp_init(struct sock *sk)
+       so->rxtimer.function = isotp_rx_timer_handler;
+       hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txtimer.function = isotp_tx_timer_handler;
++      hrtimer_init(&so->echotimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
++      so->echotimer.function = isotp_echo_timer_handler;
+       hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+       so->txfrtimer.function = isotp_txfr_timer_handler;
+-- 
+2.53.0
+
diff --git a/queue-6.6/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch b/queue-6.6/dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
new file mode 100644 (file)
index 0000000..ee85a56
--- /dev/null
@@ -0,0 +1,203 @@
+From 9f178c2e25319df6df428a77473b9b3386de5361 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 14:54:01 +0200
+Subject: dmaengine: sh: rz-dmac: Move interrupt request after everything is
+ set up
+
+From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream.
+
+Once the interrupt is requested, the interrupt handler may run immediately.
+Since the IRQ handler can access channel->ch_base, which is initialized
+only after requesting the IRQ, this may lead to invalid memory access.
+Likewise, the IRQ thread may access uninitialized data (the ld_free,
+ld_queue, and ld_active lists), which may also lead to issues.
+
+Request the interrupts only after everything is set up. To keep the error
+path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent().
+
+Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
+Cc: stable@vger.kernel.org
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Tested-by: John Madieu <john.madieu.xa@bp.renesas.com>
+Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org
+[tm: Kept the channel->irq field in rz_dmac_chan_probe() instead of
+ upstream's local `irq` variable, as commit 04e227718ab8
+ ("dmaengine: sh: rz-dmac: Make channel irq local") is not present
+ in this tree. Likewise kept platform_get_irq_byname() instead of
+ platform_get_irq_byname_optional() for the error IRQ in rz_dmac_probe(),
+ as commit 6b3a6b6dc074 ("dmaengine: sh: rz_dmac: make error interrupt
+ optional") is not present in this tree either; its early return on
+ failure becomes a goto err jump to match the new call order.]
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sh/rz-dmac.c | 96 ++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 58 deletions(-)
+
+diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
+index e5d89bc1bb83e7..63bcec46b01d7b 100644
+--- a/drivers/dma/sh/rz-dmac.c
++++ b/drivers/dma/sh/rz-dmac.c
+@@ -777,27 +777,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       channel->index = index;
+       channel->mid_rid = -EINVAL;
+-      /* Request the channel interrupt. */
+-      sprintf(pdev_irqname, "ch%u", index);
+-      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
+-      if (channel->irq < 0)
+-              return channel->irq;
+-
+-      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
+-                               dev_name(dmac->dev), index);
+-      if (!irqname)
+-              return -ENOMEM;
+-
+-      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
+-                                      rz_dmac_irq_handler,
+-                                      rz_dmac_irq_handler_thread, 0,
+-                                      irqname, channel);
+-      if (ret) {
+-              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
+-                      channel->irq, ret);
+-              return ret;
+-      }
+-
+       /* Set io base address for each channel */
+       if (index < 8) {
+               channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET +
+@@ -810,9 +789,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       }
+       /* Allocate descriptors */
+-      lmdesc = dma_alloc_coherent(&pdev->dev,
+-                                  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                  &channel->lmdesc.base_dma, GFP_KERNEL);
++      lmdesc = dmam_alloc_coherent(&pdev->dev,
++                                   sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
++                                   &channel->lmdesc.base_dma, GFP_KERNEL);
+       if (!lmdesc) {
+               dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n");
+               return -ENOMEM;
+@@ -828,7 +807,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
+       INIT_LIST_HEAD(&channel->ld_free);
+       INIT_LIST_HEAD(&channel->ld_active);
+-      return 0;
++      /* Request the channel interrupt. */
++      sprintf(pdev_irqname, "ch%u", index);
++      channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
++      if (channel->irq < 0)
++              return channel->irq;
++
++      irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
++                               dev_name(dmac->dev), index);
++      if (!irqname)
++              return -ENOMEM;
++
++      ret = devm_request_threaded_irq(dmac->dev, channel->irq,
++                                      rz_dmac_irq_handler,
++                                      rz_dmac_irq_handler_thread, 0,
++                                      irqname, channel);
++      if (ret)
++              dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
++                      channel->irq, ret);
++
++      return ret;
+ }
+ static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
+@@ -855,7 +853,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       const char *irqname = "error";
+       struct dma_device *engine;
+       struct rz_dmac *dmac;
+-      int channel_num;
+       unsigned int i;
+       int ret;
+       int irq;
+@@ -885,19 +882,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+       if (IS_ERR(dmac->ext_base))
+               return PTR_ERR(dmac->ext_base);
+-      /* Register interrupt handler for error */
+-      irq = platform_get_irq_byname(pdev, irqname);
+-      if (irq < 0)
+-              return irq;
+-
+-      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
+-                             irqname, NULL);
+-      if (ret) {
+-              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
+-                      irq, ret);
+-              return ret;
+-      }
+-
+       /* Initialize the channels. */
+       INIT_LIST_HEAD(&dmac->engine.channels);
+@@ -923,6 +907,21 @@ static int rz_dmac_probe(struct platform_device *pdev)
+                       goto err;
+       }
++      /* Register interrupt handler for error */
++      irq = platform_get_irq_byname(pdev, irqname);
++      if (irq < 0) {
++              ret = irq;
++              goto err;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
++                             irqname, NULL);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
++                      irq, ret);
++              goto err;
++      }
++
+       /* Register the DMAC as a DMA provider for DT. */
+       ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate,
+                                        NULL);
+@@ -961,16 +960,6 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ dma_register_err:
+       of_dma_controller_free(pdev->dev.of_node);
+ err:
+-      channel_num = i ? i - 1 : 0;
+-      for (i = 0; i < channel_num; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+-
+       reset_control_assert(dmac->rstc);
+ err_pm_runtime_put:
+       pm_runtime_put(&pdev->dev);
+@@ -983,18 +972,9 @@ static int rz_dmac_probe(struct platform_device *pdev)
+ static int rz_dmac_remove(struct platform_device *pdev)
+ {
+       struct rz_dmac *dmac = platform_get_drvdata(pdev);
+-      unsigned int i;
+       dma_async_device_unregister(&dmac->engine);
+       of_dma_controller_free(pdev->dev.of_node);
+-      for (i = 0; i < dmac->n_channels; i++) {
+-              struct rz_dmac_chan *channel = &dmac->channels[i];
+-
+-              dma_free_coherent(&pdev->dev,
+-                                sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
+-                                channel->lmdesc.base,
+-                                channel->lmdesc.base_dma);
+-      }
+       reset_control_assert(dmac->rstc);
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+-- 
+2.53.0
+
diff --git a/queue-6.6/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch b/queue-6.6/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
new file mode 100644 (file)
index 0000000..76533c8
--- /dev/null
@@ -0,0 +1,43 @@
+From 7ce46bd653ba5aaa90ab4a53e70508c9c7e3ebca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jun 2026 17:37:49 +0900
+Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ]
+
+__host1x_bo_unpin() drops the last reference to the mapping and frees
+it, so we can't dereference mapping afterwards. The cache itself
+outlives the mapping, so use the cache local variable instead.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/host1x/bus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
+index 6b8b7395a4189e..8a4e0738014d6c 100644
+--- a/drivers/gpu/host1x/bus.c
++++ b/drivers/gpu/host1x/bus.c
+@@ -1006,10 +1006,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+               if (WARN_ON(!cache))
+                       continue;
+-              mutex_lock(&mapping->cache->lock);
++              mutex_lock(&cache->lock);
+               WARN_ON(kref_read(&mapping->ref) != 1);
+               __host1x_bo_unpin(&mapping->ref);
+-              mutex_unlock(&mapping->cache->lock);
++              mutex_unlock(&cache->lock);
+       }
+ }
+ EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
+-- 
+2.53.0
+
diff --git a/queue-6.6/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch b/queue-6.6/input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
new file mode 100644 (file)
index 0000000..c00b82d
--- /dev/null
@@ -0,0 +1,114 @@
+From 9c11aafab37c1fa18f013d8e52a3dbd06ec33ab0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Apr 2026 09:03:59 -0700
+Subject: Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()
+
+From: Seungjin Bae <eeodqql09@gmail.com>
+
+[ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ]
+
+The `ims_pcu_process_data()` processes incoming URB data byte by byte.
+However, it fails to check if the `read_pos` index exceeds
+IMS_PCU_BUF_SIZE.
+
+If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE,
+`read_pos` will increment indefinitely. Moreover, since `read_pos` is
+located immediately after `read_buf`, the attacker can overwrite
+`read_pos` itself to arbitrarily control the index.
+
+This manipulated `read_pos` is subsequently used in
+`ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a
+heap buffer overflow.
+
+Specifically, an attacker can overwrite the `cmd_done.wait.head` located
+at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`.
+Consequently, when the driver calls `complete(&pcu->cmd_done)`, it
+triggers a control flow hijack by using the manipulated pointer.
+
+Fix this by adding a bounds check for `read_pos` before writing to
+`read_buf`. If the packet is too long, discard it, log a warning,
+and reset the parser state.
+
+Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver")
+Co-developed-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Sanghoon Choi <csh0052@gmail.com>
+Signed-off-by: Seungjin Bae <eeodqql09@gmail.com>
+Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com
+[dtor: factor out resetting packet state, reset checksum as well]
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index 2bac9d9c7b0c9c..af1dfc08b10b25 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+       }
+ }
++static void ims_pcu_reset_packet(struct ims_pcu *pcu)
++{
++      pcu->have_stx = true;
++      pcu->have_dle = false;
++      pcu->read_pos = 0;
++      pcu->check_sum = 0;
++}
++
+ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+ {
+       int i;
+@@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                       continue;
+               if (pcu->have_dle) {
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->have_dle = false;
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+@@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               dev_warn(pcu->dev,
+                                        "Unexpected STX at byte %d, discarding old data\n",
+                                        pcu->read_pos);
++                      ims_pcu_reset_packet(pcu);
+                       pcu->have_stx = true;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
+-                      pcu->check_sum = 0;
+                       break;
+               case IMS_PCU_PROTOCOL_DLE:
+@@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+                               ims_pcu_handle_response(pcu);
+                       }
+-                      pcu->have_stx = false;
+-                      pcu->have_dle = false;
+-                      pcu->read_pos = 0;
++                      ims_pcu_reset_packet(pcu);
+                       break;
+               default:
++                      if (pcu->read_pos >= IMS_PCU_BUF_SIZE) {
++                              dev_warn(pcu->dev,
++                                       "Packet too long (%d bytes), discarding\n",
++                                       pcu->read_pos);
++                              ims_pcu_reset_packet(pcu);
++                              continue;
++                      }
++
+                       pcu->read_buf[pcu->read_pos++] = data;
+                       pcu->check_sum += data;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.6/input-ims-pcu-fix-logic-error-in-packet-reset.patch b/queue-6.6/input-ims-pcu-fix-logic-error-in-packet-reset.patch
new file mode 100644 (file)
index 0000000..badfd28
--- /dev/null
@@ -0,0 +1,42 @@
+From 016625a1085ec23c05a78738fb60e62e803cef08 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 10:29:41 -0700
+Subject: Input: ims-pcu - fix logic error in packet reset
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ]
+
+ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies
+that the start-of-packet delimiter has already been received. This
+causes the protocol parser to skip waiting for the next STX byte and
+potentially process garbage data.
+
+Correctly set have_stx to false when resetting the packet state.
+
+Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/misc/ims-pcu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
+index af1dfc08b10b25..6b2aeaa50812f2 100644
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu)
+ static void ims_pcu_reset_packet(struct ims_pcu *pcu)
+ {
+-      pcu->have_stx = true;
++      pcu->have_stx = false;
+       pcu->have_dle = false;
+       pcu->read_pos = 0;
+       pcu->check_sum = 0;
+-- 
+2.53.0
+
index 74f7e26c5ad2b6fe2553969d9de665823566616f..faa55462d02c3d08bab0beb054501db32ea1fd9e 100644 (file)
@@ -11,3 +11,19 @@ kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
 kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.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
+can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
+can-isotp-serialize-tx-state-transitions-under-so-rx.patch
+dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
+accel-ivpu-reject-firmware-log-with-size-smaller-tha.patch
+gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
+xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
+input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
+input-ims-pcu-fix-logic-error-in-packet-reset.patch
diff --git a/queue-6.6/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch b/queue-6.6/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
new file mode 100644 (file)
index 0000000..2c7c56a
--- /dev/null
@@ -0,0 +1,110 @@
+From b6c0b95e96d9ce84c85df7f7413ed72277439aed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 10:14:04 -0400
+Subject: xprtrdma: Clear receive-side ownership pointers on release
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ]
+
+Three small ownership-state cleanups land the transport in a
+state that lets future reviewers reason about each pointer
+locally rather than tracing the whole reply path:
+
+rpcrdma_rep_put() clears rep->rr_rqst before the rep enters
+rb_free_reps so that no rep on the free list still carries a
+stale rqst pointer.  rpcrdma_reply_handler() and
+rpcrdma_unpin_rqst() are the only sites that set rr_rqst;
+rpcrdma_reply_handler() hands the rep through
+rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst
+directly because its error path abandons the rep for
+teardown cleanup rather than returning it to rb_free_reps.
+
+rpcrdma_reply_put() NULLs req->rl_reply before calling
+rpcrdma_rep_put().  The previous order placed the rep on
+rb_free_reps while req->rl_reply still pointed at it; the
+window was harmless because xprt_rdma_free_slot() holds the
+req exclusively across the pair, but closing it makes the
+invariant 'rep on rb_free_reps implies no req references it'
+strictly checkable.
+
+rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear
+req->rl_sendctx after dropping the sendctx pointer in the
+sendctx ring.  Without this, req->rl_sendctx survives across
+Send completion and points at a sendctx that may already have
+been reassigned by rpcrdma_sendctx_get_locked() to a different
+req.  No caller dereferences the stale pointer today --
+rpcrdma_prepare_send_sges() overwrites it before the next
+Send -- but a NULL is a more honest representation of 'the
+Send is no longer outstanding' and lets the assertion patch
+that follows trip on any future regression.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sunrpc/xprtrdma/rpc_rdma.c |  4 ++++
+ net/sunrpc/xprtrdma/verbs.c    | 12 ++++++++++--
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
+index e201b37578a70e..aa57e057ff451f 100644
+--- a/net/sunrpc/xprtrdma/rpc_rdma.c
++++ b/net/sunrpc/xprtrdma/rpc_rdma.c
+@@ -542,6 +542,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+       rpcrdma_req_put(req);
+ }
+@@ -550,8 +551,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+  */
+ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+ {
++      struct rpcrdma_req *req = sc->sc_req;
++
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+ }
+ /* Prepare an SGE for the RPC-over-RDMA transport header.
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index 27bb176f082f63..a97f0b18ac4294 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1067,9 +1067,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
+  * @buf: buffer pool
+  * @rep: rep to release
+  *
++ * The rep's transient association with an rpc_rqst, established
++ * by rpcrdma_reply_handler() and torn down here, must not survive
++ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
++ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
++ * would imply the rep is still referenced by a req.
+  */
+ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
+ {
++      rep->rr_rqst = NULL;
+       llist_add(&rep->rr_node, &buf->rb_free_reps);
+ }
+@@ -1252,9 +1258,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
+  */
+ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
+ {
+-      if (req->rl_reply) {
+-              rpcrdma_rep_put(buffers, req->rl_reply);
++      struct rpcrdma_rep *rep = req->rl_reply;
++
++      if (rep) {
+               req->rl_reply = NULL;
++              rpcrdma_rep_put(buffers, rep);
+       }
+       /* I2: rl_reply NULL after the put closes the
+        * 'rep on rb_free_reps still referenced by req' window.
+-- 
+2.53.0
+
diff --git a/queue-7.1/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch b/queue-7.1/crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
new file mode 100644 (file)
index 0000000..959a0ba
--- /dev/null
@@ -0,0 +1,44 @@
+From 49b831a033720b4388bc2e321b0fc6e0994f68ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:34:52 +0900
+Subject: crypto: tegra - Don't touch bo refcount in host1x bo pin/unpin
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit f8c9c57d750346abd213ffed2ae3cacb0268e9f1 ]
+
+Since commit "gpu: host1x: Allow entries in BO caches to be freed",
+host1x_bo_pin() and host1x_bo_unpin() handle the bo's refcount
+themselves. .pin/.unpin callbacks should not adjust it.
+
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/crypto/tegra/tegra-se-main.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c
+index e8d8c3a23d7adb..d2f518ef9a103f 100644
+--- a/drivers/crypto/tegra/tegra-se-main.c
++++ b/drivers/crypto/tegra/tegra-se-main.c
+@@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire
+               return ERR_PTR(-ENOMEM);
+       kref_init(&map->ref);
+-      map->bo = host1x_bo_get(bo);
++      map->bo = bo;
+       map->direction = direction;
+       map->dev = dev;
+@@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map)
+       dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
+       sg_free_table(map->sgt);
+       kfree(map->sgt);
+-      host1x_bo_put(map->bo);
+       kfree(map);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch b/queue-7.1/gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
new file mode 100644 (file)
index 0000000..f324ec3
--- /dev/null
@@ -0,0 +1,43 @@
+From a8786a94ae4ee4314be261630cacfc8c62a22db1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jun 2026 17:37:49 +0900
+Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings
+
+From: Mikko Perttunen <mperttunen@nvidia.com>
+
+[ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ]
+
+__host1x_bo_unpin() drops the last reference to the mapping and frees
+it, so we can't dereference mapping afterwards. The cache itself
+outlives the mapping, so use the cache local variable instead.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u
+Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/host1x/bus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
+index c273a4476f2370..a8cddb3425b428 100644
+--- a/drivers/gpu/host1x/bus.c
++++ b/drivers/gpu/host1x/bus.c
+@@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+               if (WARN_ON(!cache))
+                       continue;
+-              mutex_lock(&mapping->cache->lock);
++              mutex_lock(&cache->lock);
+               WARN_ON(kref_read(&mapping->ref) != 1);
+               __host1x_bo_unpin(&mapping->ref);
+-              mutex_unlock(&mapping->cache->lock);
++              mutex_unlock(&cache->lock);
+       }
+ }
+ EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
+-- 
+2.53.0
+
index 00801c7732d8aa7b6edb1f0fa9d9d05cb4281eab..b1e281a171631d556ee29913428887b82de06175 100644 (file)
@@ -7,3 +7,6 @@ kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
 kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
 kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
 kvm-x86-mmu-fix-use-after-free-on-vendor-module-reload.patch
+gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
+crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
+xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
diff --git a/queue-7.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch b/queue-7.1/xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
new file mode 100644 (file)
index 0000000..da8a28d
--- /dev/null
@@ -0,0 +1,110 @@
+From 25c8d70bccdabeef0a328a45e038f8e345640762 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 10:14:04 -0400
+Subject: xprtrdma: Clear receive-side ownership pointers on release
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ]
+
+Three small ownership-state cleanups land the transport in a
+state that lets future reviewers reason about each pointer
+locally rather than tracing the whole reply path:
+
+rpcrdma_rep_put() clears rep->rr_rqst before the rep enters
+rb_free_reps so that no rep on the free list still carries a
+stale rqst pointer.  rpcrdma_reply_handler() and
+rpcrdma_unpin_rqst() are the only sites that set rr_rqst;
+rpcrdma_reply_handler() hands the rep through
+rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst
+directly because its error path abandons the rep for
+teardown cleanup rather than returning it to rb_free_reps.
+
+rpcrdma_reply_put() NULLs req->rl_reply before calling
+rpcrdma_rep_put().  The previous order placed the rep on
+rb_free_reps while req->rl_reply still pointed at it; the
+window was harmless because xprt_rdma_free_slot() holds the
+req exclusively across the pair, but closing it makes the
+invariant 'rep on rb_free_reps implies no req references it'
+strictly checkable.
+
+rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear
+req->rl_sendctx after dropping the sendctx pointer in the
+sendctx ring.  Without this, req->rl_sendctx survives across
+Send completion and points at a sendctx that may already have
+been reassigned by rpcrdma_sendctx_get_locked() to a different
+req.  No caller dereferences the stale pointer today --
+rpcrdma_prepare_send_sges() overwrites it before the next
+Send -- but a NULL is a more honest representation of 'the
+Send is no longer outstanding' and lets the assertion patch
+that follows trip on any future regression.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sunrpc/xprtrdma/rpc_rdma.c |  4 ++++
+ net/sunrpc/xprtrdma/verbs.c    | 12 ++++++++++--
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
+index e8b5fc86a01bbf..81070b78512b1e 100644
+--- a/net/sunrpc/xprtrdma/rpc_rdma.c
++++ b/net/sunrpc/xprtrdma/rpc_rdma.c
+@@ -493,6 +493,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+       rpcrdma_req_put(req);
+ }
+@@ -501,8 +502,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+  */
+ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+ {
++      struct rpcrdma_req *req = sc->sc_req;
++
+       rpcrdma_sendctx_dma_unmap(sc);
+       sc->sc_req = NULL;
++      req->rl_sendctx = NULL;
+ }
+ /* Prepare an SGE for the RPC-over-RDMA transport header.
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index 0ac82f26bf6ec9..cbd20bef367d96 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1095,9 +1095,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
+  * @buf: buffer pool
+  * @rep: rep to release
+  *
++ * The rep's transient association with an rpc_rqst, established
++ * by rpcrdma_reply_handler() and torn down here, must not survive
++ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
++ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
++ * would imply the rep is still referenced by a req.
+  */
+ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
+ {
++      rep->rr_rqst = NULL;
+       llist_add(&rep->rr_node, &buf->rb_free_reps);
+ }
+@@ -1280,9 +1286,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
+  */
+ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
+ {
+-      if (req->rl_reply) {
+-              rpcrdma_rep_put(buffers, req->rl_reply);
++      struct rpcrdma_rep *rep = req->rl_reply;
++
++      if (rep) {
+               req->rl_reply = NULL;
++              rpcrdma_rep_put(buffers, rep);
+       }
+       /* I2: rl_reply NULL after the put closes the
+        * 'rep on rb_free_reps still referenced by req' window.
+-- 
+2.53.0
+