[PATCH v15 10/11] can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler()
Oliver Hartkopp via B4 Relay <[email protected]> Tue, 14 Jul 2026 18:55:32 +0200
| Newsgroups | org.kernel.vger.linux-can,org.kernel.feeds.b4-sent |
|---|---|
| Message-ID | <[email protected]> |
From: Oliver Hartkopp <[email protected]> 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: [email protected] Link: https://lore.kernel.org/linux-can/[email protected]/ Signed-off-by: Oliver Hartkopp <[email protected]> --- 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 a53dba6ab8b8..f213a0b37791 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -798,15 +798,10 @@ 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); rtr_frame = op->flags & RX_RTR_FRAME; if (rtr_frame) { @@ -834,10 +829,18 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_LOCAL; if (skb->sk == op->sk) 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, traffic_flags); goto rx_starttimer; -- 2.53.0