From: Jun Yang <[email protected]>
__br_multicast_disable_port_ctx() and br_multicast_del_port() walk
port->mglist with hlist_for_each_entry_safe(). However,
br_multicast_find_del_pg() can also delete other entries from the same
list through br_multicast_fwd_src_remove() or __fwd_del_star_excl().
If such an entry is the iterator's saved next node, hlist_del_init()
clears its ->next and terminates the walk early. The reproducer triggers
this in both teardown walks, leaving port groups in the bridge mdb with
a dangling ->key.port after del_nbp() frees the port:
BUG: KASAN: slab-use-after-free in __mdb_fill_info+0x1191/0x1320
__mdb_fill_info+0x1191/0x1320
br_mdb_dump+0x594/0xe40
rtnl_mdb_dump+0x1cf/0x5d0
Use hlist_del_init_rcu() to unlink the group while preserving ->next.
br_multicast_del_pg() and the teardown walks run under
br->multicast_lock. The GC worker must acquire the same lock before
detaching the group for destruction, so the node remains alive while
the walk uses the preserved pointer.
Fixes: b08123684bd5 ("net: bridge: mcast: install S,G entries automatically
based on reports")
Cc: [email protected]
Suggested-by: Nikolay Aleksandrov <[email protected]>
Reported-by: TencentOS Corvus AI <[email protected]>
Assisted-by: tencentos-corvus-ai:kimi-k3
Signed-off-by: Jun Yang <[email protected]>
---
v2: delete with hlist_del_init_rcu() to preserve ->next for an in-progress
teardown walk, per Nikolay's suggestion, instead of restarting the walks.
Tested on 7.2-rc7 with KASAN: the original use-after-free is no longer
reported.
v1: https://lore.kernel.org/all/[email protected]/
net/bridge/br_multicast.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 00aa9b2879d6..8022c7f12169 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
struct hlist_node *tmp;
rcu_assign_pointer(*pp, pg->next);
- hlist_del_init(&pg->mglist);
+ /* Keep ->next (held under multicast_lock, freed later by the GC work):
+ * a port->mglist teardown walk may have latched this node as its next,
+ * and deleting other groups of the same port must not truncate it.
+ */
+ hlist_del_init_rcu(&pg->mglist);
br_multicast_eht_clean_sets(pg);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent, false);
--
2.43.7