All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list
@ 2026-10-16  9:11 Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 1/3] can: isotp: fix filter removal race between release and netns change Oliver Hartkopp
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Oliver Hartkopp @ 2026-10-16  9:11 UTC (permalink / raw)
  To: linux-can; +Cc: Oliver Hartkopp

Norbert Szetei reported a use-after-free in isotp: isotp_release()
removes the socket from the netdev notifier list before it takes
rtnl_lock(). A socket that leaves the list in this window is skipped by
the notifier and has to remove its CAN filters itself. If the bound
device is moved to another network namespace in this window,
can_rx_unregister() silently skips the removal due to the namespace
mismatch and the remaining receivers point to the freed socket.

bcm has the same window and is affected in the same way. raw has the
window too, but removes its filters with dev_net(ro->dev) under RTNL
and is therefore not affected.

This series takes rtnl_lock() before the socket leaves the notifier
list in all three protocols. As the notifier chain is always invoked
under RTNL, the busy-wait loops on the *_busy_notifier variables are
dead code and are replaced with WARN_ON_ONCE().

For bcm, RTNL is only held for the notifier list removal and the
removal of the device specific filters. The procfs entry, the tx_ops
and the 'any' CAN device filters are removed after RTNL is released,
when bcm_notify() can not access the socket anymore.

Tested with PROVE_LOCKING, KASAN and DEBUG_ATOMIC_SLEEP while creating,
binding and releasing raw/bcm/isotp sockets on a vcan device that is
continuously moved between two network namespaces, set down and
re-created, with concurrent CAN traffic and bcm procfs readers.
A bcm procfs reader stalled via userfaultfd does not block RTNL while
bcm_release() waits for it in remove_proc_entry(), also not with a
concurrent removal of a CAN netdev in the socket's namespace.

Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/

Changes in v3:
- bcm: remove the socket from the notifier list and the device specific
  filters under RTNL first. Then remove the procfs entry, the tx_ops and
  the 'any' CAN device filters without RTNL. In v2 a stalled procfs
  reader could still block bcm_notify() (under RTNL) on lock_sock()
  while bcm_release() waited in remove_proc_entry(), and the 'any' CAN
  device filters were removed under RTNL.
  Clearing bo->bcm_proc_read in bcm_release() is not needed anymore.
- bcm: replace the stale comment about a concurrent NETDEV_UNREGISTER
  in the rx_ops loop of bcm_release()

Changes in v2:
- bcm: remove the procfs entry and the tx_ops before taking RTNL so
  that remove_proc_entry() can not stall RTNL waiting for readers of
  the world-readable procfs entry
- bcm, raw: reword the commit messages so that they do not refer to
  patches outside of this series
- send all three patches as one series for can-next

Oliver Hartkopp (3):
  can: isotp: fix filter removal race between release and netns change
  can: bcm: fix filter removal race between release and netns change
  can: raw: take rtnl_lock() before leaving the notifier list

 net/can/bcm.c   | 47 +++++++++++++++++++++--------------------------
 net/can/isotp.c | 17 +++++++++--------
 net/can/raw.c   | 13 +++++++------
 3 files changed, 37 insertions(+), 40 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH can-next v3 1/3] can: isotp: fix filter removal race between release and netns change
  2026-10-16  9:11 [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
@ 2026-10-16  9:11 ` Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 2/3] can: bcm: " Oliver Hartkopp
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Oliver Hartkopp @ 2026-10-16  9:11 UTC (permalink / raw)
  To: linux-can; +Cc: Oliver Hartkopp, Norbert Szetei, stable

isotp_release() removes the socket from isotp_notifier_list before it
takes rtnl_lock(). As the netdev notifier chain runs under RTNL, a
socket that leaves the list in this window is skipped by isotp_notify()
and has to remove its CAN filters itself.

This fails when the bound device is moved to another network namespace
in this window: isotp_release() passes sock_net(sk) to
can_rx_unregister(), which silently returns when this namespace does not
match dev_net(dev) anymore. As can_rx_register() stores rcv->sk without
taking a reference, the remaining receivers point to the freed socket
and travel with the device into the new namespace:

  BUG: KASAN: use-after-free in isotp_rcv+0x1570/0x24d0
  Read of size 1 at addr ffff888118130552 by task isotp_ns_uaf/578
   can_rcv_filter+0x4af/0x8c0
   can_receive+0x28d/0x3c0
   can_rcv+0x2a9/0x310
   __netif_receive_skb_one_core+0x21a/0x260
   process_backlog+0x210/0x760

Take rtnl_lock() before removing the socket from the notifier list, so
that isotp_release() and isotp_notify() can not both skip the removal.

As the notifier chain is always invoked under RTNL (see ASSERT_RTNL() in
call_netdevice_notifiers_info()), isotp_busy_notifier can not point to
the released socket anymore. Replace the now dead busy-wait loop with a
WARN_ON_ONCE(). isotp_notifier_lock is still needed, as isotp_init()
adds new sockets to isotp_notifier_list without holding RTNL.

Additionally use dev_net(so->dev) for the can_rx_unregister() calls, as
the CAN filters live in the device's namespace. With the new locking
both namespaces are equal there, but dev_net(so->dev) is stable under
RTNL and always names the correct namespace.

Fixes: 20bab8b88baa ("can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER")
Reported-by: Norbert Szetei <norbert@doyensec.com>
Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
 net/can/isotp.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/net/can/isotp.c b/net/can/isotp.c
index f5dc9d0..186daa8 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -1479,16 +1479,17 @@ static int isotp_release(struct socket *sock)
 	/* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */
 	wake_up_interruptible(&so->wait);
 
+	rtnl_lock();
+
+	/* The netdev notifier chain runs under RTNL, so isotp_notifier() can't
+	 * be walking isotp_notifier_list and point to this socket. The spinlock
+	 * is still needed as isotp_init() adds sockets without holding RTNL.
+	 */
 	spin_lock(&isotp_notifier_lock);
-	while (isotp_busy_notifier == so) {
-		spin_unlock(&isotp_notifier_lock);
-		schedule_timeout_uninterruptible(1);
-		spin_lock(&isotp_notifier_lock);
-	}
+	WARN_ON_ONCE(isotp_busy_notifier == so);
 	list_del(&so->notifier);
 	spin_unlock(&isotp_notifier_lock);
 
-	rtnl_lock();
 	lock_sock(sk);
 
 	/* remove current filters & unregister
@@ -1496,11 +1497,11 @@ static int isotp_release(struct socket *sock)
 	 */
 	if (so->bound && so->dev) {
 		if (isotp_register_rxid(so))
-			can_rx_unregister(net, so->dev, so->rxid,
+			can_rx_unregister(dev_net(so->dev), so->dev, so->rxid,
 					  SINGLE_MASK(so->rxid),
 					  isotp_rcv, sk);
 
-		can_rx_unregister(net, so->dev, so->txid,
+		can_rx_unregister(dev_net(so->dev), so->dev, so->txid,
 				  SINGLE_MASK(so->txid),
 				  isotp_rcv_echo, sk);
 		netdev_put(so->dev, &so->dev_tracker);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH can-next v3 2/3] can: bcm: fix filter removal race between release and netns change
  2026-10-16  9:11 [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 1/3] can: isotp: fix filter removal race between release and netns change Oliver Hartkopp
@ 2026-10-16  9:11 ` Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 3/3] can: raw: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
  2026-10-16 13:00 ` [PATCH can-next v3 0/3] can: " Marc Kleine-Budde
  3 siblings, 0 replies; 5+ messages in thread
From: Oliver Hartkopp @ 2026-10-16  9:11 UTC (permalink / raw)
  To: linux-can; +Cc: Oliver Hartkopp, Norbert Szetei, stable

bcm_release() removes the socket from bcm_notifier_list before it takes
any lock that serializes against the netdev notifier chain, which runs
under RTNL. A socket that leaves the list in this window is skipped by
bcm_notify() and has to remove its CAN filters itself.

This fails when the bound device is moved to another network namespace
in this window: bcm_rx_unreg() reads dev_net(dev) without holding RTNL,
and can_rx_unregister() silently returns when this namespace does not
match dev_net(dev) anymore. The op is freed anyway and the remaining
receiver leads to a use-after-free in bcm_rx_handler().

Take rtnl_lock() before removing the socket from the notifier list, so
that bcm_release() and bcm_notify() can not both skip the removal and
dev_net(dev) is stable while the device specific filters are removed.
As the notifier chain is always invoked under RTNL, bcm_busy_notifier
can not point to the released socket anymore. Replace the now dead
busy-wait loop with a WARN_ON_ONCE().

RTNL is only held for the notifier list removal and the removal of the
device specific filters. Once the socket has left the notifier list,
bcm_notify() can not access it anymore. So the procfs entry, the tx_ops
and the filters for 'any' CAN device, which live in the socket's
namespace, are removed after RTNL has been released. This way a stalled
reader of the world-readable procfs entry can not hold up RTNL, and the
removal of the 'any' CAN device filters does not delay RTNL anymore.

Fixes: 8e8cda6d737d ("can: initial support for network namespaces")
Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/
Cc: Norbert Szetei <norbert@doyensec.com>
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
 net/can/bcm.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3d637a1..b1bb3db 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1926,17 +1926,30 @@ static int bcm_release(struct socket *sock)
 
 	/* remove bcm_ops, timer, rx_unregister(), etc. */
 
+	rtnl_lock();
+
+	/* The netdev notifier chain runs under RTNL, so bcm_notifier() can't
+	 * be walking bcm_notifier_list and point to this socket. The spinlock
+	 * is still needed as bcm_init() adds sockets without holding RTNL.
+	 */
 	spin_lock(&bcm_notifier_lock);
-	while (bcm_busy_notifier == bo) {
-		spin_unlock(&bcm_notifier_lock);
-		schedule_timeout_uninterruptible(1);
-		spin_lock(&bcm_notifier_lock);
-	}
+	WARN_ON_ONCE(bcm_busy_notifier == bo);
 	list_del(&bo->notifier);
 	spin_unlock(&bcm_notifier_lock);
 
 	lock_sock(sk);
 
+	/* Remove the device specific subscriptions under RTNL */
+	list_for_each_entry(op, &bo->rx_ops, list)
+		if (op->ifindex && op->rx_reg_dev)
+			bcm_rx_unreg(op->rx_reg_dev, op);
+
+	rtnl_unlock();
+
+	/* bcm_notify() can not access this socket anymore. Release the other
+	 * resources without holding RTNL, e.g. remove_proc_entry() waits for
+	 * concurrent readers of the procfs entry.
+	 */
 #if IS_ENABLED(CONFIG_PROC_FS)
 	/* remove procfs entry */
 	if (net->can.bcmproc_dir && bo->bcm_proc_read)
@@ -1948,31 +1961,13 @@ static int bcm_release(struct socket *sock)
 		bcm_remove_op(op);
 	}
 
-	list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
-		/*
-		 * Don't care if we're bound or not (due to netdev problems)
-		 * can_rx_unregister() is always a save thing to do here.
-		 */
-		if (op->ifindex) {
-			/*
-			 * Only remove subscriptions that had not
-			 * been removed due to NETDEV_UNREGISTER
-			 * in bcm_notifier()
-			 *
-			 * op->rx_reg_dev is a tracked reference taken
-			 * when the subscription was registered, so it
-			 * stays valid here even if a concurrent
-			 * NETDEV_UNREGISTER already unlisted the device.
-			 */
-			if (op->rx_reg_dev)
-				bcm_rx_unreg(op->rx_reg_dev, op);
-		} else
+	/* 'any' CAN device subscriptions live in the socket's namespace */
+	list_for_each_entry(op, &bo->rx_ops, list)
+		if (!op->ifindex)
 			can_rx_unregister(net, NULL, op->can_id,
 					  REGMASK(op->can_id),
 					  bcm_rx_handler, op);
 
-	}
-
 	synchronize_rcu();
 
 	list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH can-next v3 3/3] can: raw: take rtnl_lock() before leaving the notifier list
  2026-10-16  9:11 [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 1/3] can: isotp: fix filter removal race between release and netns change Oliver Hartkopp
  2026-10-16  9:11 ` [PATCH can-next v3 2/3] can: bcm: " Oliver Hartkopp
@ 2026-10-16  9:11 ` Oliver Hartkopp
  2026-10-16 13:00 ` [PATCH can-next v3 0/3] can: " Marc Kleine-Budde
  3 siblings, 0 replies; 5+ messages in thread
From: Oliver Hartkopp @ 2026-10-16  9:11 UTC (permalink / raw)
  To: linux-can; +Cc: Oliver Hartkopp

raw_release() removes the socket from raw_notifier_list before it takes
rtnl_lock(). As the netdev notifier chain runs under RTNL, a socket that
leaves the list in this window is skipped by raw_notify() and has to
remove its CAN filters itself.

For raw this is not harmful: raw_release() removes the filters with
dev_net(ro->dev) under RTNL, so they are found even when the bound
device has been moved to another network namespace in this window. The
receivers only remain on the moved device until raw_release() gets
RTNL.

Take rtnl_lock() before removing the socket from the notifier list, so
that raw, isotp and bcm use the same locking scheme in their release
functions. As the notifier chain is always invoked under RTNL,
raw_busy_notifier can not point to the released socket anymore. Replace
the now dead busy-wait loop with a WARN_ON_ONCE().

Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
 net/can/raw.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/net/can/raw.c b/net/can/raw.c
index 0acd4f6..925c0c1 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -422,16 +422,17 @@ static int raw_release(struct socket *sock)
 	ro = raw_sk(sk);
 	net = sock_net(sk);
 
+	rtnl_lock();
+
+	/* The netdev notifier chain runs under RTNL, so raw_notifier() can't
+	 * be walking raw_notifier_list and point to this socket. The spinlock
+	 * is still needed as raw_init() adds sockets without holding RTNL.
+	 */
 	spin_lock(&raw_notifier_lock);
-	while (raw_busy_notifier == ro) {
-		spin_unlock(&raw_notifier_lock);
-		schedule_timeout_uninterruptible(1);
-		spin_lock(&raw_notifier_lock);
-	}
+	WARN_ON_ONCE(raw_busy_notifier == ro);
 	list_del(&ro->notifier);
 	spin_unlock(&raw_notifier_lock);
 
-	rtnl_lock();
 	lock_sock(sk);
 
 	/* remove current filters & unregister */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list
  2026-10-16  9:11 [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
                   ` (2 preceding siblings ...)
  2026-10-16  9:11 ` [PATCH can-next v3 3/3] can: raw: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
@ 2026-10-16 13:00 ` Marc Kleine-Budde
  3 siblings, 0 replies; 5+ messages in thread
From: Marc Kleine-Budde @ 2026-10-16 13:00 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: linux-can

[-- Attachment #1: Type: text/plain, Size: 2132 bytes --]

On 16.10.2026 11:11:25, Oliver Hartkopp wrote:
> Norbert Szetei reported a use-after-free in isotp: isotp_release()
> removes the socket from the netdev notifier list before it takes
> rtnl_lock(). A socket that leaves the list in this window is skipped by
> the notifier and has to remove its CAN filters itself. If the bound
> device is moved to another network namespace in this window,
> can_rx_unregister() silently skips the removal due to the namespace
> mismatch and the remaining receivers point to the freed socket.
>
> bcm has the same window and is affected in the same way. raw has the
> window too, but removes its filters with dev_net(ro->dev) under RTNL
> and is therefore not affected.
>
> This series takes rtnl_lock() before the socket leaves the notifier
> list in all three protocols. As the notifier chain is always invoked
> under RTNL, the busy-wait loops on the *_busy_notifier variables are
> dead code and are replaced with WARN_ON_ONCE().
>
> For bcm, RTNL is only held for the notifier list removal and the
> removal of the device specific filters. The procfs entry, the tx_ops
> and the 'any' CAN device filters are removed after RTNL is released,
> when bcm_notify() can not access the socket anymore.
>
> Tested with PROVE_LOCKING, KASAN and DEBUG_ATOMIC_SLEEP while creating,
> binding and releasing raw/bcm/isotp sockets on a vcan device that is
> continuously moved between two network namespaces, set down and
> re-created, with concurrent CAN traffic and bcm procfs readers.
> A bcm procfs reader stalled via userfaultfd does not block RTNL while
> bcm_release() waits for it in remove_proc_entry(), also not with a
> concurrent removal of a CAN netdev in the socket's namespace.
>
> Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/

Applied.

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-16 13:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16  9:11 [PATCH can-next v3 0/3] can: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
2026-10-16  9:11 ` [PATCH can-next v3 1/3] can: isotp: fix filter removal race between release and netns change Oliver Hartkopp
2026-10-16  9:11 ` [PATCH can-next v3 2/3] can: bcm: " Oliver Hartkopp
2026-10-16  9:11 ` [PATCH can-next v3 3/3] can: raw: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
2026-10-16 13:00 ` [PATCH can-next v3 0/3] can: " Marc Kleine-Budde

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.