From: Oliver Hartkopp <socketcan@hartkopp.net>
To: linux-can@vger.kernel.org
Cc: Oliver Hartkopp <socketcan@hartkopp.net>,
Norbert Szetei <norbert@doyensec.com>,
stable@vger.kernel.org
Subject: [PATCH can-next v3 2/3] can: bcm: fix filter removal race between release and netns change
Date: Fri, 16 Oct 2026 11:11:27 +0200 [thread overview]
Message-ID: <20261016091128.325332-3-socketcan@hartkopp.net> (raw)
In-Reply-To: <20261016091128.325332-1-socketcan@hartkopp.net>
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
next prev parent reply other threads:[~2026-10-16 9:11 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
2026-10-16 9:11 ` [PATCH can-next v3 3/3] can: raw: take rtnl_lock() before leaving the notifier list Oliver Hartkopp
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261016091128.325332-3-socketcan@hartkopp.net \
--to=socketcan@hartkopp.net \
--cc=linux-can@vger.kernel.org \
--cc=norbert@doyensec.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox