[PATCH v15 05/11] can: bcm: add missing rcu list annotations and operations
Oliver Hartkopp via B4 Relay <[email protected]> Tue, 14 Jul 2026 18:55:27 +0200
| Newsgroups | org.kernel.vger.linux-can,dev.linux.lists.sashiko-reviews,org.kernel.feeds.b4-sent |
|---|---|
| Message-ID | <[email protected]> |
From: Oliver Hartkopp <[email protected]> sashiko-bot remarked the missing use of list_add_rcu() in bcm_[rx|tx]_setup() to have a proper initialized bcm_op structure when bcm_proc_show() traverses the bcm_op's under rcu_read_lock(). To cover all initial settings of the bcm_op's the list_add_rcu() calls are moved to the end of the setup code. While at it, also fix the mirroring removal side: bcm_release() called bcm_remove_op() - which frees the op via call_rcu() - on ops that were still linked in bo->tx_ops/bo->rx_ops, without list_del_rcu() first. Unlink each op with list_del_rcu() before handing it to bcm_remove_op(), matching the existing pattern in bcm_delete_tx_op()/bcm_delete_rx_op(). Reported-by: [email protected] Closes: https://lore.kernel.org/linux-can/[email protected]/ Fixes: dac5e6249159 ("can: bcm: add missing rcu read protection for procfs content") Signed-off-by: Oliver Hartkopp <[email protected]> --- net/can/bcm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 03c98e4cc677..5c1e83eeb4ff 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -263,11 +263,11 @@ static int bcm_proc_show(struct seq_file *m, void *v) seq_printf(m, "%s%ld%%\n", (reduction == 100) ? "near " : "", reduction); } - list_for_each_entry(op, &bo->tx_ops, list) { + list_for_each_entry_rcu(op, &bo->tx_ops, list) { seq_printf(m, "tx_op: %03X %s ", op->can_id, bcm_proc_getifname(net, ifname, op->ifindex)); if (op->flags & CAN_FD_FRAME) @@ -1015,10 +1015,11 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, int ifindex, struct sock *sk) { struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; struct canfd_frame *cf; + bool add_op_to_list = false; unsigned int i; int err; /* we need a real device to send frames */ if (!ifindex) @@ -1156,12 +1157,11 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* currently unused in tx_ops */ hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the tx_ops */ - list_add(&op->list, &bo->tx_ops); + add_op_to_list = true; } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */ if (op->flags & SETTIMER) { /* set timer values */ @@ -1179,10 +1179,14 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_cancel(&op->timer); /* spec: send CAN frame when starting timer */ op->flags |= TX_ANNOUNCE; } + /* add this bcm_op to the list of the tx_ops? */ + if (add_op_to_list) + list_add_rcu(&op->list, &bo->tx_ops); + if (op->flags & TX_ANNOUNCE) bcm_can_tx(op, NULL); if (op->flags & STARTTIMER) bcm_tx_start_timer(op); @@ -1371,13 +1375,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->timer, bcm_rx_timeout_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the rx_ops */ - list_add(&op->list, &bo->rx_ops); - /* call can_rx_register() */ do_rx_register = 1; } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */ @@ -1447,14 +1448,16 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = can_rx_register(sock_net(sk), NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */ - list_del_rcu(&op->list); bcm_remove_op(op); return err; } + + /* add this bcm_op to the list of the rx_ops */ + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; } @@ -1784,12 +1787,14 @@ static int bcm_release(struct socket *sock) /* remove procfs entry */ if (net->can.bcmproc_dir && bo->bcm_proc_read) remove_proc_entry(bo->procname, net->can.bcmproc_dir); #endif /* CONFIG_PROC_FS */ - list_for_each_entry_safe(op, next, &bo->tx_ops, list) + list_for_each_entry_safe(op, next, &bo->tx_ops, list) { + list_del_rcu(&op->list); 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. @@ -1816,12 +1821,14 @@ static int bcm_release(struct socket *sock) } synchronize_rcu(); - list_for_each_entry_safe(op, next, &bo->rx_ops, list) + list_for_each_entry_safe(op, next, &bo->rx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } /* remove device reference */ if (bo->bound) { WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; -- 2.53.0