diff options
| author | Li RongQing <lirongqing@baidu.com> | 2026-09-17 13:20:12 +0800 |
|---|---|---|
| committer | Leon Romanovsky <leon@kernel.org> | 2026-09-23 06:38:36 -0400 |
| commit | d1fa82ef992bc18850d27bc0bdc7a5cd0821d7b3 (patch) | |
| tree | 1fd941add398a7158daf61a2585313c767ed5a14 /drivers/infiniband/core | |
| parent | ef15197f81ffedf29560edf2221199d92fd83dc2 (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.c | 7 |
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); |
