Re: [PATCH net] can: bcm: add missing synchronize_rcu() in bcm_delete_rx_op()

Oliver Hartkopp <[email protected]> Mon, 27 Jul 2026 10:57:23 +0200
Newsgroups org.kernel.vger.linux-can,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>

On 27.07.26 10:37, Nguyen Le Thanh Tung wrote:
> bcm_delete_rx_op(), reachable from an unprivileged user via an RX_DELETE
> bcm_msg_head, unregisters the CAN rx handler and then immediately calls
> bcm_remove_op(), which cancels op->timer and frees the op via call_rcu():
> 
> 	can_rx_unregister(..., bcm_rx_handler, op);
> 	list_del_rcu(&op->list);
> 	bcm_remove_op(op);		/* hrtimer_cancel() + call_rcu() */
> 
> bcm_rx_handler() runs in an RCU read-side critical section and re-arms
> op->timer on frame reception via bcm_rx_starttimer(). A handler that is
> already in flight on another CPU when RX_DELETE is processed can be past
> its RX_NO_AUTOTIMER check -- a plain, unordered u32 store set a few lines
> earlier in bcm_delete_rx_op() -- and re-arm the timer after bcm_remove_op()
> has cancelled it. The op is then freed after the grace period with its
> hrtimer still enqueued, so the timer fires on freed memory in the timer
> softirq: an out-of-bounds access that can crash the machine or disclose
> adjacent kernel memory. AF_CAN BCM sockets are reachable in a user +
> network namespace (e.g. over a vcan interface), so this is triggerable
> by an unprivileged local user.
> 
> commit d5f9023fa61e ("can: bcm: delay release of struct bcm_op after
> synchronize_rcu()") fixed exactly this race for the socket teardown path
> in bcm_release(), by draining in-flight handlers with synchronize_rcu()
> between can_rx_unregister() and bcm_remove_op(), but the RX_DELETE path in
> bcm_delete_rx_op() was left unchanged and still has the race.
> 
> Add the same synchronize_rcu() to bcm_delete_rx_op() so that no
> bcm_rx_handler() can be running -- and thus re-arm the timer -- once
> bcm_remove_op() cancels it and frees the op. bcm_delete_rx_op() is only
> called from bcm_sendmsg() under lock_sock() in process context, so
> sleeping in synchronize_rcu() is safe here, exactly as it already is in
> bcm_release().
> 
> Fixes: d5f9023fa61e ("can: bcm: delay release of struct bcm_op after synchronize_rcu()")
> Signed-off-by: Nguyen Le Thanh Tung <[email protected]>

NAK

This is already fixed with

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/can/bcm.c?id=68973f9db76144825e4f35dfdc80fb8279eb2d57
("can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF")

without re-introducing the costly synchronize_rcu().

There are a bunch of bcm fixes that have been applied to 7.2-rc3 and are 
queued for several stable trees. Please re-check the current bcm.c 
developments and fixes.

Best regards,
Oliver

> ---
>   net/can/bcm.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
> 
> diff --git a/net/can/bcm.c b/net/can/bcm.c
> index 75653584f..25f868530 100644
> --- a/net/can/bcm.c
> +++ b/net/can/bcm.c
> @@ -822,6 +822,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
>   						  bcm_rx_handler, op);
>   
>   			list_del_rcu(&op->list);
> +
> +			/* wait for any in-flight bcm_rx_handler() to finish so a
> +			 * concurrent frame reception cannot re-arm op->timer after
> +			 * bcm_remove_op() has cancelled it and the op is freed
> +			 * (same drain as the bcm_release() teardown path).
> +			 */
> +			synchronize_rcu();
> +
>   			bcm_remove_op(op);
>   			return 1; /* done */
>   		}