Skip to content

Commit f5449e7

Browse files
committed
RDMA/ucma: Rework ucma_migrate_id() to avoid races with destroy
ucma_destroy_id() assumes that all things accessing the ctx will do so via the xarray. This assumption violated only in the case the FD is being closed, then the ctx is reached via the ctx_list. Normally this is OK since ucma_destroy_id() cannot run concurrenty with release(), however with ucma_migrate_id() is involved this can violated as the close of the 2nd FD can run concurrently with destroy on the first: CPU0 CPU1 ucma_destroy_id(fda) ucma_migrate_id(fda -> fdb) ucma_get_ctx() xa_lock() _ucma_find_context() xa_erase() xa_unlock() xa_lock() ctx->file = new_file list_move() xa_unlock() ucma_put_ctx() ucma_close(fdb) _destroy_id() kfree(ctx) _destroy_id() wait_for_completion() // boom, ctx was freed The ctx->file must be modified under the handler and xa_lock, and prior to modification the ID must be rechecked that it is still reachable from cur_file, ie there is no parallel destroy or migrate. To make this work remove the double locking and streamline the control flow. The double locking was obsoleted by the handler lock now directly preventing new uevents from being created, and the ctx_list cannot be read while holding fgets on both files. Removing the double locking also removes the need to check for the same file. Fixes: 88314e4 ("RDMA/cma: add support for rdma_migrate_id()") Link: https://lore.kernel.org/r/[email protected] Reported-and-tested-by: [email protected] Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 8383da3 commit f5449e7

File tree

1 file changed

+29
-50
lines changed

1 file changed

+29
-50
lines changed

drivers/infiniband/core/ucma.c

Lines changed: 29 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,45 +1587,15 @@ static ssize_t ucma_leave_multicast(struct ucma_file *file,
15871587
return ret;
15881588
}
15891589

1590-
static void ucma_lock_files(struct ucma_file *file1, struct ucma_file *file2)
1591-
{
1592-
/* Acquire mutex's based on pointer comparison to prevent deadlock. */
1593-
if (file1 < file2) {
1594-
mutex_lock(&file1->mut);
1595-
mutex_lock_nested(&file2->mut, SINGLE_DEPTH_NESTING);
1596-
} else {
1597-
mutex_lock(&file2->mut);
1598-
mutex_lock_nested(&file1->mut, SINGLE_DEPTH_NESTING);
1599-
}
1600-
}
1601-
1602-
static void ucma_unlock_files(struct ucma_file *file1, struct ucma_file *file2)
1603-
{
1604-
if (file1 < file2) {
1605-
mutex_unlock(&file2->mut);
1606-
mutex_unlock(&file1->mut);
1607-
} else {
1608-
mutex_unlock(&file1->mut);
1609-
mutex_unlock(&file2->mut);
1610-
}
1611-
}
1612-
1613-
static void ucma_move_events(struct ucma_context *ctx, struct ucma_file *file)
1614-
{
1615-
struct ucma_event *uevent, *tmp;
1616-
1617-
list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list)
1618-
if (uevent->ctx == ctx)
1619-
list_move_tail(&uevent->list, &file->event_list);
1620-
}
1621-
16221590
static ssize_t ucma_migrate_id(struct ucma_file *new_file,
16231591
const char __user *inbuf,
16241592
int in_len, int out_len)
16251593
{
16261594
struct rdma_ucm_migrate_id cmd;
16271595
struct rdma_ucm_migrate_resp resp;
1596+
struct ucma_event *uevent, *tmp;
16281597
struct ucma_context *ctx;
1598+
LIST_HEAD(event_list);
16291599
struct fd f;
16301600
struct ucma_file *cur_file;
16311601
int ret = 0;
@@ -1641,43 +1611,52 @@ static ssize_t ucma_migrate_id(struct ucma_file *new_file,
16411611
ret = -EINVAL;
16421612
goto file_put;
16431613
}
1614+
cur_file = f.file->private_data;
16441615

16451616
/* Validate current fd and prevent destruction of id. */
1646-
ctx = ucma_get_ctx(f.file->private_data, cmd.id);
1617+
ctx = ucma_get_ctx(cur_file, cmd.id);
16471618
if (IS_ERR(ctx)) {
16481619
ret = PTR_ERR(ctx);
16491620
goto file_put;
16501621
}
16511622

16521623
rdma_lock_handler(ctx->cm_id);
1653-
cur_file = ctx->file;
1654-
if (cur_file == new_file) {
1655-
mutex_lock(&cur_file->mut);
1656-
resp.events_reported = ctx->events_reported;
1657-
mutex_unlock(&cur_file->mut);
1658-
goto response;
1659-
}
1660-
16611624
/*
1662-
* Migrate events between fd's, maintaining order, and avoiding new
1663-
* events being added before existing events.
1625+
* ctx->file can only be changed under the handler & xa_lock. xa_load()
1626+
* must be checked again to ensure the ctx hasn't begun destruction
1627+
* since the ucma_get_ctx().
16641628
*/
1665-
ucma_lock_files(cur_file, new_file);
16661629
xa_lock(&ctx_table);
1667-
1668-
list_move_tail(&ctx->list, &new_file->ctx_list);
1669-
ucma_move_events(ctx, new_file);
1630+
if (_ucma_find_context(cmd.id, cur_file) != ctx) {
1631+
xa_unlock(&ctx_table);
1632+
ret = -ENOENT;
1633+
goto err_unlock;
1634+
}
16701635
ctx->file = new_file;
1636+
xa_unlock(&ctx_table);
1637+
1638+
mutex_lock(&cur_file->mut);
1639+
list_del(&ctx->list);
1640+
/*
1641+
* At this point lock_handler() prevents addition of new uevents for
1642+
* this ctx.
1643+
*/
1644+
list_for_each_entry_safe(uevent, tmp, &cur_file->event_list, list)
1645+
if (uevent->ctx == ctx)
1646+
list_move_tail(&uevent->list, &event_list);
16711647
resp.events_reported = ctx->events_reported;
1648+
mutex_unlock(&cur_file->mut);
16721649

1673-
xa_unlock(&ctx_table);
1674-
ucma_unlock_files(cur_file, new_file);
1650+
mutex_lock(&new_file->mut);
1651+
list_add_tail(&ctx->list, &new_file->ctx_list);
1652+
list_splice_tail(&event_list, &new_file->event_list);
1653+
mutex_unlock(&new_file->mut);
16751654

1676-
response:
16771655
if (copy_to_user(u64_to_user_ptr(cmd.response),
16781656
&resp, sizeof(resp)))
16791657
ret = -EFAULT;
16801658

1659+
err_unlock:
16811660
rdma_unlock_handler(ctx->cm_id);
16821661
ucma_put_ctx(ctx);
16831662
file_put:

0 commit comments

Comments
 (0)