Skip to content

Commit 36e8169

Browse files
rleonjgunthorpe
authored andcommitted
RDMA/ucma: Protect mc during concurrent multicast leaves
Partially revert the commit mentioned in the Fixes line to make sure that allocation and erasing multicast struct are locked. BUG: KASAN: use-after-free in ucma_cleanup_multicast drivers/infiniband/core/ucma.c:491 [inline] BUG: KASAN: use-after-free in ucma_destroy_private_ctx+0x914/0xb70 drivers/infiniband/core/ucma.c:579 Read of size 8 at addr ffff88801bb74b00 by task syz-executor.1/25529 CPU: 0 PID: 25529 Comm: syz-executor.1 Not tainted 5.16.0-rc7-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106 print_address_description.constprop.0.cold+0x8d/0x320 mm/kasan/report.c:247 __kasan_report mm/kasan/report.c:433 [inline] kasan_report.cold+0x83/0xdf mm/kasan/report.c:450 ucma_cleanup_multicast drivers/infiniband/core/ucma.c:491 [inline] ucma_destroy_private_ctx+0x914/0xb70 drivers/infiniband/core/ucma.c:579 ucma_destroy_id+0x1e6/0x280 drivers/infiniband/core/ucma.c:614 ucma_write+0x25c/0x350 drivers/infiniband/core/ucma.c:1732 vfs_write+0x28e/0xae0 fs/read_write.c:588 ksys_write+0x1ee/0x250 fs/read_write.c:643 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae Currently the xarray search can touch a concurrently freeing mc as the xa_for_each() is not surrounded by any lock. Rather than hold the lock for a full scan hold it only for the effected items, which is usually an empty list. Fixes: 95fe510 ("RDMA/ucma: Remove mc_list and rely on xarray") Link: https://lore.kernel.org/r/1cda5fabb1081e8d16e39a48d3a4f8160cea88b8.1642491047.git.leonro@nvidia.com Reported-by: syzbot+e3f96c43d19782dd14a7@syzkaller.appspotmail.com Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Maor Gottlieb <maorg@nvidia.com> Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent d9e410e commit 36e8169

File tree

1 file changed

+23
-11
lines changed

1 file changed

+23
-11
lines changed

drivers/infiniband/core/ucma.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ struct ucma_context {
9595
u64 uid;
9696

9797
struct list_head list;
98+
struct list_head mc_list;
9899
struct work_struct close_work;
99100
};
100101

@@ -105,6 +106,7 @@ struct ucma_multicast {
105106

106107
u64 uid;
107108
u8 join_state;
109+
struct list_head list;
108110
struct sockaddr_storage addr;
109111
};
110112

@@ -198,6 +200,7 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file)
198200

199201
INIT_WORK(&ctx->close_work, ucma_close_id);
200202
init_completion(&ctx->comp);
203+
INIT_LIST_HEAD(&ctx->mc_list);
201204
/* So list_del() will work if we don't do ucma_finish_ctx() */
202205
INIT_LIST_HEAD(&ctx->list);
203206
ctx->file = file;
@@ -484,19 +487,19 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
484487

485488
static void ucma_cleanup_multicast(struct ucma_context *ctx)
486489
{
487-
struct ucma_multicast *mc;
488-
unsigned long index;
490+
struct ucma_multicast *mc, *tmp;
489491

490-
xa_for_each(&multicast_table, index, mc) {
491-
if (mc->ctx != ctx)
492-
continue;
492+
xa_lock(&multicast_table);
493+
list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) {
494+
list_del(&mc->list);
493495
/*
494496
* At this point mc->ctx->ref is 0 so the mc cannot leave the
495497
* lock on the reader and this is enough serialization
496498
*/
497-
xa_erase(&multicast_table, index);
499+
__xa_erase(&multicast_table, mc->id);
498500
kfree(mc);
499501
}
502+
xa_unlock(&multicast_table);
500503
}
501504

502505
static void ucma_cleanup_mc_events(struct ucma_multicast *mc)
@@ -1469,12 +1472,16 @@ static ssize_t ucma_process_join(struct ucma_file *file,
14691472
mc->uid = cmd->uid;
14701473
memcpy(&mc->addr, addr, cmd->addr_size);
14711474

1472-
if (xa_alloc(&multicast_table, &mc->id, NULL, xa_limit_32b,
1475+
xa_lock(&multicast_table);
1476+
if (__xa_alloc(&multicast_table, &mc->id, NULL, xa_limit_32b,
14731477
GFP_KERNEL)) {
14741478
ret = -ENOMEM;
14751479
goto err_free_mc;
14761480
}
14771481

1482+
list_add_tail(&mc->list, &ctx->mc_list);
1483+
xa_unlock(&multicast_table);
1484+
14781485
mutex_lock(&ctx->mutex);
14791486
ret = rdma_join_multicast(ctx->cm_id, (struct sockaddr *)&mc->addr,
14801487
join_state, mc);
@@ -1500,8 +1507,11 @@ static ssize_t ucma_process_join(struct ucma_file *file,
15001507
mutex_unlock(&ctx->mutex);
15011508
ucma_cleanup_mc_events(mc);
15021509
err_xa_erase:
1503-
xa_erase(&multicast_table, mc->id);
1510+
xa_lock(&multicast_table);
1511+
list_del(&mc->list);
1512+
__xa_erase(&multicast_table, mc->id);
15041513
err_free_mc:
1514+
xa_unlock(&multicast_table);
15051515
kfree(mc);
15061516
err_put_ctx:
15071517
ucma_put_ctx(ctx);
@@ -1569,15 +1579,17 @@ static ssize_t ucma_leave_multicast(struct ucma_file *file,
15691579
mc = ERR_PTR(-EINVAL);
15701580
else if (!refcount_inc_not_zero(&mc->ctx->ref))
15711581
mc = ERR_PTR(-ENXIO);
1572-
else
1573-
__xa_erase(&multicast_table, mc->id);
1574-
xa_unlock(&multicast_table);
15751582

15761583
if (IS_ERR(mc)) {
1584+
xa_unlock(&multicast_table);
15771585
ret = PTR_ERR(mc);
15781586
goto out;
15791587
}
15801588

1589+
list_del(&mc->list);
1590+
__xa_erase(&multicast_table, mc->id);
1591+
xa_unlock(&multicast_table);
1592+
15811593
mutex_lock(&mc->ctx->mutex);
15821594
rdma_leave_multicast(mc->ctx->cm_id, (struct sockaddr *) &mc->addr);
15831595
mutex_unlock(&mc->ctx->mutex);

0 commit comments

Comments
 (0)