summaryrefslogtreecommitdiff
path: root/drivers/infiniband/core
diff options
context:
space:
mode:
authorLi RongQing <lirongqing@baidu.com>2026-09-17 13:20:12 +0800
committerLeon Romanovsky <leon@kernel.org>2026-09-23 06:38:36 -0400
commitd1fa82ef992bc18850d27bc0bdc7a5cd0821d7b3 (patch)
tree1fd941add398a7158daf61a2585313c767ed5a14 /drivers/infiniband/core
parentef15197f81ffedf29560edf2221199d92fd83dc2 (diff)
RDMA/multicast: Fix rb-tree corruption on MGID reassignment in join_handler
When the SA assigns a wildcard (mgid0) join a concrete MGID that already has a group in the port table, join_handler() erases the group from the tree and calls mcast_insert() with allow_duplicates=false. mcast_insert() then finds the existing group and returns it without re-inserting, but join_handler() discards the return value, leaving the group erased yet still alive. When the orphaned group's refcount later drops to zero, release_group() calls rb_erase() on the node that is no longer in the tree, corrupting the rb-tree (a stale parent pointer causes a valid subtree link to be overwritten with NULL). Clear the node with RB_CLEAR_NODE() after the erase in join_handler() so release_group() can detect via RB_EMPTY_NODE() that it is already out of the tree and skip the second erase. A successful re-insert by mcast_insert() restores the in-tree state via rb_link_node(). Signed-off-by: Li RongQing <lirongqing@baidu.com> Link: https://patch.msgid.link/20260917052012.2181-1-lirongqing@baidu.com Signed-off-by: Leon Romanovsky <leon@kernel.org>
Diffstat (limited to 'drivers/infiniband/core')
-rw-r--r--drivers/infiniband/core/multicast.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index bea7df3dd8f3..f549fd928c1c 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -189,7 +189,10 @@ static void release_group(struct mcast_group *group)
spin_lock_irqsave(&port->lock, flags);
if (atomic_dec_and_test(&group->refcount)) {
- rb_erase(&group->node, &port->table);
+
+ if (!RB_EMPTY_NODE(&group->node))
+ rb_erase(&group->node, &port->table);
+
spin_unlock_irqrestore(&port->lock, flags);
kfree(group);
deref_port(port);
@@ -533,6 +536,8 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
group->rec = *rec;
if (mgids_changed) {
rb_erase(&group->node, &group->port->table);
+
+ RB_CLEAR_NODE(&group->node);
is_mgid0 = !memcmp(&mgid0, &group->rec.mgid,
sizeof(mgid0));
mcast_insert(group->port, group, is_mgid0);