From a88c02915d9c6160cfc7ab1b26ed64b2993e2b94 Mon Sep 17 00:00:00 2001 From: Lim HyeonJun Date: Sun, 24 May 2026 20:08:53 +0900 Subject: [PATCH] io_uring/tctx: set ->io_uring before publishing the tctx node io_register_iowq_max_workers() walks ctx->tctx_list under ctx->tctx_lock and dereferences each node's task->io_uring without a NULL check: list_for_each_entry(node, &ctx->tctx_list, ctx_node) { tctx = node->task->io_uring; if (WARN_ON_ONCE(!tctx->io_wq)) continue; ... } __io_uring_add_tctx_node() installs the node into ctx->tctx_list (via io_tctx_install_node(), which does the list_add() under tctx_lock) and only assigns current->io_uring = tctx afterwards. A task doing its first io_uring operation on a shared ring therefore has a window in which its node is already visible on ctx->tctx_list while node->task->io_uring is still NULL. A concurrent IORING_REGISTER_IOWQ_MAX_WORKERS on the same ring reads that NULL and dereferences tctx->io_wq: KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f] RIP: io_register_iowq_max_workers io_uring/register.c:423 Publish current->io_uring = tctx before installing the node, so any node visible on ctx->tctx_list always has a valid task->io_uring. Fixes: 7880174e1e5e ("io_uring/tctx: clean up __io_uring_add_tctx_node() error handling") Signed-off-by: Lim HyeonJun Link: https://patch.msgid.link/20260524110853.115634-1-shja0831@gmail.com Signed-off-by: Jens Axboe --- io_uring/tctx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/io_uring/tctx.c b/io_uring/tctx.c index 6af62ca9baba..42b219b34aa8 100644 --- a/io_uring/tctx.c +++ b/io_uring/tctx.c @@ -139,12 +139,14 @@ static int io_tctx_install_node(struct io_ring_ctx *ctx, int __io_uring_add_tctx_node(struct io_ring_ctx *ctx) { struct io_uring_task *tctx = current->io_uring; + bool new_tctx = false; int ret; if (unlikely(!tctx)) { tctx = io_uring_alloc_task_context(current, ctx); if (IS_ERR(tctx)) return PTR_ERR(tctx); + new_tctx = true; if (data_race(ctx->int_flags) & IO_RING_F_IOWQ_LIMITS_SET) { unsigned int limits[2]; @@ -168,13 +170,15 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx) if (tctx->io_wq) io_wq_set_exit_on_idle(tctx->io_wq, false); - ret = io_tctx_install_node(ctx, tctx); - if (!ret) { + if (new_tctx) current->io_uring = tctx; + + ret = io_tctx_install_node(ctx, tctx); + if (!ret) return 0; - } - if (!current->io_uring) { err_free: + if (new_tctx) { + current->io_uring = NULL; if (tctx->io_wq) { io_wq_exit_start(tctx->io_wq); io_wq_put_and_exit(tctx->io_wq);