aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorNikolay Aleksandrov <razor@blackwall.org>2026-09-03 12:38:51 +0300
committerJakub Kicinski <kuba@kernel.org>2026-09-04 15:29:47 -0700
commit4b772869a1e5f9da5cef5b9c722ec0aa424ee0a0 (patch)
tree9b70da42cbddbf5bbd2ce661a7c541710cfe1cf4
parentMerge branch 'eth-fix-bugs-in-ntuple-filter-reporting' (diff)
net: bridge: mcast: properly convert mglist to rcu
Sashiko reported a bug [1] that br_multicast_del_port_group unlists the port group not using proper rcu helper that preserves the next pointer and after that immediately frees the port group without waiting for rcu grace period. The only rcu walker of mglist is br_multicast_list_adjacent() and it turns out that function has always been buggy because mglist was never properly converted to RCU. Fix it by converting it to rcu and moving its initialization after eth_addr's. Initializing p->next can use RCU_INIT_POINTER because we have a barrier from the hlist_add_head_rcu call later, besides we're initializing an unpublished structure anyway. [1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260826014200.362304-1-littleddfu%40gmail.com Fixes: 07f8ac4a1e26 ("bridge: add export of multicast database adjacent to net_dev") Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260903093851.1494297-1-razor@blackwall.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/bridge/br_multicast.c9
1 files changed, 5 insertions, 4 deletions
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 3e9b10f8abf1..2f9bb30e1a1f 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1441,16 +1441,17 @@ struct net_bridge_port_group *br_multicast_new_port_group(
goto free_out;
}
- rcu_assign_pointer(p->next, next);
timer_setup(&p->timer, br_multicast_port_group_expired, 0);
timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
- hlist_add_head(&p->mglist, &port->mglist);
if (src)
memcpy(p->eth_addr, src, ETH_ALEN);
else
eth_broadcast_addr(p->eth_addr);
+ RCU_INIT_POINTER(p->next, next);
+ hlist_add_head_rcu(&p->mglist, &port->mglist);
+
return p;
free_out:
@@ -1465,11 +1466,11 @@ void br_multicast_del_port_group(struct net_bridge_port_group *p)
struct net_bridge_port *port = p->key.port;
__u16 vid = p->key.addr.vid;
- hlist_del_init(&p->mglist);
+ hlist_del_init_rcu(&p->mglist);
if (!br_multicast_is_star_g(&p->key.addr))
rhashtable_remove_fast(&port->br->sg_port_tbl, &p->rhnode,
br_sg_port_rht_params);
- kfree(p);
+ kfree_rcu(p, rcu);
br_multicast_port_ngroups_dec(port, vid);
}