Skip to content

Commit c1e2148

Browse files
committed
io_uring: free fixed_file_data after RCU grace period
The percpu refcount protects this structure, and we can have an atomic switch in progress when exiting. This makes it unsafe to just free the struct normally, and can trigger the following KASAN warning: BUG: KASAN: use-after-free in percpu_ref_switch_to_atomic_rcu+0xfa/0x1b0 Read of size 1 at addr ffff888181a19a30 by task swapper/0/0 CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.6.0-rc4+ #5747 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 Call Trace: <IRQ> dump_stack+0x76/0xa0 print_address_description.constprop.0+0x3b/0x60 ? percpu_ref_switch_to_atomic_rcu+0xfa/0x1b0 ? percpu_ref_switch_to_atomic_rcu+0xfa/0x1b0 __kasan_report.cold+0x1a/0x3d ? percpu_ref_switch_to_atomic_rcu+0xfa/0x1b0 percpu_ref_switch_to_atomic_rcu+0xfa/0x1b0 rcu_core+0x370/0x830 ? percpu_ref_exit+0x50/0x50 ? rcu_note_context_switch+0x7b0/0x7b0 ? run_rebalance_domains+0x11d/0x140 __do_softirq+0x10a/0x3e9 irq_exit+0xd5/0xe0 smp_apic_timer_interrupt+0x86/0x200 apic_timer_interrupt+0xf/0x20 </IRQ> RIP: 0010:default_idle+0x26/0x1f0 Fix this by punting the final exit and free of the struct to RCU, then we know that it's safe to do so. Jann suggested the approach of using a double rcu callback to achieve this. It's important that we do a nested call_rcu() callback, as otherwise the free could be ordered before the atomic switch, even if the latter was already queued. Reported-by: [email protected] Suggested-by: Jann Horn <[email protected]> Reviewed-by: Paul E. McKenney <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 80ad894 commit c1e2148

File tree

1 file changed

+22
-2
lines changed

1 file changed

+22
-2
lines changed

fs/io_uring.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ struct fixed_file_data {
191191
struct llist_head put_llist;
192192
struct work_struct ref_work;
193193
struct completion done;
194+
struct rcu_head rcu;
194195
};
195196

196197
struct io_ring_ctx {
@@ -5329,6 +5330,26 @@ static void io_file_ref_kill(struct percpu_ref *ref)
53295330
complete(&data->done);
53305331
}
53315332

5333+
static void __io_file_ref_exit_and_free(struct rcu_head *rcu)
5334+
{
5335+
struct fixed_file_data *data = container_of(rcu, struct fixed_file_data,
5336+
rcu);
5337+
percpu_ref_exit(&data->refs);
5338+
kfree(data);
5339+
}
5340+
5341+
static void io_file_ref_exit_and_free(struct rcu_head *rcu)
5342+
{
5343+
/*
5344+
* We need to order our exit+free call against the potentially
5345+
* existing call_rcu() for switching to atomic. One way to do that
5346+
* is to have this rcu callback queue the final put and free, as we
5347+
* could otherwise have a pre-existing atomic switch complete _after_
5348+
* the free callback we queued.
5349+
*/
5350+
call_rcu(rcu, __io_file_ref_exit_and_free);
5351+
}
5352+
53325353
static int io_sqe_files_unregister(struct io_ring_ctx *ctx)
53335354
{
53345355
struct fixed_file_data *data = ctx->file_data;
@@ -5341,14 +5362,13 @@ static int io_sqe_files_unregister(struct io_ring_ctx *ctx)
53415362
flush_work(&data->ref_work);
53425363
wait_for_completion(&data->done);
53435364
io_ring_file_ref_flush(data);
5344-
percpu_ref_exit(&data->refs);
53455365

53465366
__io_sqe_files_unregister(ctx);
53475367
nr_tables = DIV_ROUND_UP(ctx->nr_user_files, IORING_MAX_FILES_TABLE);
53485368
for (i = 0; i < nr_tables; i++)
53495369
kfree(data->table[i].files);
53505370
kfree(data->table);
5351-
kfree(data);
5371+
call_rcu(&data->rcu, io_file_ref_exit_and_free);
53525372
ctx->file_data = NULL;
53535373
ctx->nr_user_files = 0;
53545374
return 0;

0 commit comments

Comments
 (0)