Linux CAN drivers development
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: mkl@pengutronix.de
Cc: netdev@vger.kernel.org, davem@davemloft.net, kuba@kernel.org,
	linux-can@vger.kernel.org, kernel@pengutronix.de,
	socketcan@hartkopp.net, kurt.van.dijck@eia.be
Subject: Re: [PATCH net-next 08/37] can: bcm: fix filter removal race between release and netns change
Date: Fri, 16 Oct 2026 02:37:05 +0000	[thread overview]
Message-ID: <179211822523.434549.241636889494644724@kernel.org> (raw)
In-Reply-To: <20261015015336.816543-9-mkl@pengutronix.de>

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The commit moves remove_proc_entry() ahead of rtnl_lock() so
  that procfs readers cannot stall RTNL from close().
- [Medium] bcm_release() now holds the global rtnl_mutex across the whole
  rx_ops unregistration loop (rtnl_lock() at net/can/bcm.c:1951,…
- [Low] Stale comment in bcm_release().

--

can: bcm: fix filter removal race between release and netns change

bcm_release() now takes rtnl_lock() before it removes the socket from
bcm_notifier_list. It holds RTNL across the rx_ops unregistration, so
bcm_release() and bcm_notify() can no longer both skip filter removal
while the bound device moves to another netns. The bcm_busy_notifier
busy-wait becomes a WARN_ON_ONCE(), and the procfs entry and tx_ops are
now removed before RTNL is taken.

Link: https://lore.kernel.org/linux-can/CEA6A38A-2646-4ADA-95B4-CBAE2F301A8E@doyensec.com/
Link: https://patch.msgid.link/20261014142114.280279-3-socketcan@hartkopp.net

> diff --git a/net/can/bcm.c b/net/can/bcm.c
> index 6759662e024c..301920011a30 100644
> --- a/net/can/bcm.c
> +++ b/net/can/bcm.c
> @@ -1926,21 +1926,19 @@ static int bcm_release(struct socket *sock)
>  
>  	/* remove bcm_ops, timer, rx_unregister(), etc. */
>  
> -	spin_lock(&bcm_notifier_lock);
> -	while (bcm_busy_notifier == bo) {
> -		spin_unlock(&bcm_notifier_lock);
> -		schedule_timeout_uninterruptible(1);
> -		spin_lock(&bcm_notifier_lock);
> -	}
> -	list_del(&bo->notifier);
> -	spin_unlock(&bcm_notifier_lock);
> -
> +	/* Remove the procfs entry and the tx_ops before taking RTNL:
> +	 * remove_proc_entry() waits for concurrent readers of the entry and
> +	 * must not hold up RTNL for them. Clear bo->bcm_proc_read so that
> +	 * bcm_notify() does not remove the entry a second time.
> +	 */
>  	lock_sock(sk);
>  
>  #if IS_ENABLED(CONFIG_PROC_FS)
>  	/* remove procfs entry */
> -	if (net->can.bcmproc_dir && bo->bcm_proc_read)
> +	if (net->can.bcmproc_dir && bo->bcm_proc_read) {
>  		remove_proc_entry(bo->procname, net->can.bcmproc_dir);
> +		bo->bcm_proc_read = NULL;
> +	}
>  #endif /* CONFIG_PROC_FS */

[Severity: Medium]
list_del(&bo->notifier) now runs after rtnl_lock(). That means this
first lock_sock() section runs while the socket is still on
bcm_notifier_list. Can a procfs reader still stall RTNL here, just
indirectly through the socket lock?

Consider this interleaving:

CPU1 (close)
bcm_release()
  lock_sock(sk)
  remove_proc_entry()
    proc_entry_rundown()
      wait_for_completion()      <- waits for the reader on CPU2

CPU2 (reader of /proc/net/can-bcm/<ino>)
proc_reg_read()
  use_pde()
  pde_read()                     <- copy to a FUSE or userfaultfd
                                    backed buffer blocks

CPU3 (NETDEV_UNREGISTER, RTNL held)
bcm_notifier()
  bcm_notify(bo, NETDEV_UNREGISTER, dev)
    lock_sock(sk)                <- blocks behind CPU1

bcm_notify() takes lock_sock(sk) before it checks whether any op or the
binding uses dev. So deleting or moving any CAN device in the socket's
netns would be enough.

Before this patch, the socket was unlinked first and the busy-wait made
sure bcm_notify() was not running for it, so this interleaving was not
possible.

The procfs entry is mode 0644 and is created on any connect(), including
can_ifindex = 0. With unprivileged user namespaces, a vcan device in a
user-owned netns can trigger the notifier, and RTNL is global.

Does this mean the commit message goal ("which could otherwise stall
RTNL from a plain close()") is only partly met?

There are similar RTNL stall paths already. bcm_sendmsg() holds
lock_sock() across memcpy_from_msg() in bcm_tx_setup() and
bcm_rx_setup(), and bcm_notify() itself calls remove_proc_entry() under
RTNL. This particular ordering is new with this patch, though.

[ ... ]

> @@ -1948,6 +1946,21 @@ static int bcm_release(struct socket *sock)
>  		bcm_remove_op(op);
>  	}
>  
> +	release_sock(sk);
> +
> +	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);
> +	WARN_ON_ONCE(bcm_busy_notifier == bo);
> +	list_del(&bo->notifier);
> +	spin_unlock(&bcm_notifier_lock);
> +
> +	lock_sock(sk);
> +
>  	list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
>  		/*
>  		 * Don't care if we're bound or not (due to netdev problems)

[Severity: Low]
This isn't a functional problem, but the comment a bit further down in
this rx_ops loop in bcm_release() still describes a concurrent
unregister:

			 * 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.

This loop now runs between rtnl_lock() and rtnl_unlock(), and the socket
stays on bcm_notifier_list until RTNL is taken. A NETDEV_UNREGISTER has
either already finished, in which case bcm_notify()->bcm_rx_unreg()
cleared op->rx_reg_dev, or it runs after this loop, when the socket is
no longer on the list.

Can a concurrent NETDEV_UNREGISTER still happen at this point? The old
comment seems to contradict the new comment added above it.

The same comment in bcm_delete_rx_op() is still accurate, because that
path does not hold RTNL. Should the copy in bcm_release() be updated?

> @@ -1973,6 +1986,8 @@ static int bcm_release(struct socket *sock)
>  
>  	}
>  
> +	rtnl_unlock();
> +
>  	synchronize_rcu();

[Severity: Medium]
The global rtnl_mutex is now held across the whole rx_ops
unregistration loop, and userspace decides how long that loop is. Is
that intended?

bcm_rx_setup() allocates one op per distinct (can_id, ifindex) with
kzalloc(OPSIZ, GFP_KERNEL). That allocation is not memcg accounted, and
there is no per-socket limit. bcm_connect() accepts can_ifindex == 0
without any device, and ANYDEV ops register on the all-devices list
through:

	err = can_rx_register(sock_net(sk), NULL, op->can_id,

So an unprivileged user with no CAN hardware can create a very large
number of ops.

On close(), each op calls can_rx_unregister(). That takes
spin_lock_bh(&net->can.rcvlists_lock) and walks the receiver hlist
linearly until it finds the match.

A second socket can put M newer entries in front of the victim's
entries in the same bucket. It can do this with EFF ids that fold to
the same bucket in effhash(), or with the same SFF id. RTNL would then
be held for roughly K * M list steps.

Before this patch, the same loop ran only under lock_sock(sk), so only
the closing task paid for it. Now it would block netlink and netdev
configuration in every network namespace.

The loop without cond_resched() already existed, and the attacker needs
a lot of kernel memory. What is new is that RTNL covers the whole loop.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261015015336.816543-2-mkl%40pengutronix.de

  reply	other threads:[~2026-10-16  2:37 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15  1:39 [PATCH net-next 0/37] pull-request: can-next 2026-10-15 Marc Kleine-Budde
2026-10-15  1:39 ` [PATCH net-next 01/37] can: dev: can_dropped_invalid_skb: drop CAN XL frames on non-CAN XL devices Marc Kleine-Budde
2026-10-15  1:57   ` netdev-bot+sinfo
2026-10-15  1:40 ` [PATCH net-next 02/37] can: raw: remove redundant NULL check before netdev_hold() Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 03/37] can: replace ARPHRD_CAN type checks with can_get_ml_priv() Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 04/37] can: proc: reset pkg_stats atomics individually Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 05/37] can: proc: remove pointers from CAN specific proc output Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 06/37] can: isotp: check the frame type, not just the length Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 07/37] can: isotp: fix filter removal race between release and netns change Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 08/37] can: bcm: " Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko [this message]
2026-10-15  1:40 ` [PATCH net-next 09/37] can: raw: take rtnl_lock() before leaving the notifier list Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 10/37] can: skb: make echo skb freeing safe in any IRQ context Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 11/37] dt-bindings: can: renesas,rcar-canfd: Document RZ/G3S SoC Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 12/37] can: skb: make CAN skb allocation failure paths IRQ-safe Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 13/37] can: rcar_canfd: Fix typos in macro names Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 14/37] can: dev: can_put_echo_skb(): free skb on invalid echo index Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 15/37] can: rcar_canfd: Allow the CAN FD clock to be sourced from fck Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 16/37] can: rcar_canfd: Do not set registers selecting the CAN mode Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 17/37] can: rcar_canfd: Add support for Renesas RZ/G3S Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 18/37] dt-bindings: can: renesas,rcar-canfd: Restrict resets in top-level Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 19/37] dt-bindings: can: renesas,rcar-canfd: Document RZ/G3L SoC Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 20/37] can: rcar_canfd: Derive max_channels from the device tree Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 21/37] dt-bindings: net: can: convert grcan to DT schema Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 22/37] can: rcar_canfd: Add support for Renesas RZ/G3L Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 23/37] can: grcan: update the binding file reference in the driver comment Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 24/37] can: remove Softing CANcard driver Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 25/37] can: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 26/37] can: cc770: don't discard the IRQ lookup error in probe Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 27/37] can: cc770: fix the clock divider check on the platform bus Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 28/37] can: ems_usb: use usb_kill_urb() to stop the intr URB Marc Kleine-Budde
2026-10-16  2:37   ` netdev-bot+sashiko
2026-10-15  1:40 ` [PATCH net-next 29/37] can: esd: acc_start_xmit(): avoid use-after-free of skb after can_put_echo_skb() Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-15  1:40 ` [PATCH net-next 30/37] can: flexcan: flexcan_setup_stop_mode_gpr: fix OF node reference leak Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 31/37] can: kvaser_usb: refactor endpoint lookup Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 32/37] can: kvaser_usb: validate command format before parsing in hydra receive path Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 33/37] can: kvaser_pciefd: fix use-after-free in bec poll timer Marc Kleine-Budde
2026-10-16  1:54   ` sashiko-bot
2026-10-15  1:40 ` [PATCH net-next 34/37] can: mcp251xfd: mcp251xfd_probe(): reject devices without match data Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 35/37] can: sun4i_can: sun4ican_probe(): fix clk leak Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 36/37] can: ucan: fix repeated word 'is' in comment Marc Kleine-Budde
2026-10-15  1:40 ` [PATCH net-next 37/37] can: xilinx_can: set CAN FD flags on received frames Marc Kleine-Budde

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=179211822523.434549.241636889494644724@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=davem@davemloft.net \
    --cc=kernel@pengutronix.de \
    --cc=kuba@kernel.org \
    --cc=kurt.van.dijck@eia.be \
    --cc=linux-can@vger.kernel.org \
    --cc=mkl@pengutronix.de \
    --cc=netdev@vger.kernel.org \
    --cc=socketcan@hartkopp.net \
    /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