]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
io_uring/zcrx: don't clear master_ctx from the import path
authorWoraphat Khiaodaeng <worapat.kd2@gmail.com>
Thu, 30 Jul 2026 16:27:41 +0000 (16:27 +0000)
committerJens Axboe <axboe@kernel.dk>
Thu, 30 Jul 2026 19:18:06 +0000 (13:18 -0600)
import_zcrx() attaches an existing ifq to another ring.  It never calls
zcrx_set_ring_ctx() and so never takes the ->master_ctx reference, but
its error path still passes @ctx to zcrx_unregister(), which clears
->master_ctx and drops its percpu_ref whenever ifq->master_ctx == ctx.

That condition is reachable.  A ring that registers an ifq with a
non-zero event type_mask gets ->master_ctx pointed at itself, and
nothing stops it from exporting that ifq with ZCRX_CTRL_EXPORT and
importing the resulting fd back into the same ring.  Failing the import
after the refcount bumps -- an argument page mapped PROT_READ makes the
copy_to_user() in import_zcrx() return -EFAULT -- then clears the
->master_ctx owned by the original registration, which is still live.

Refcounts stay balanced and nothing is freed early, so there is no
splat.  The ring silently stops receiving ZCRX_EVENT_ALLOC_FAIL and
ZCRX_EVENT_COPY: zcrx_send_notif() returns early on a NULL
->master_ctx, and ->master_ctx is only ever set on a freshly allocated
ifq, so it cannot be restored without tearing the ring down.

Pass NULL instead, matching zcrx_box_release() and the zcrx_export()
error path.  io_register_zcrx() only gets away with passing @ctx
because zcrx_set_ring_ctx() runs after its last goto err.

Fixes: 00d91481279f ("io_uring/zcrx: share an ifq between rings")
Signed-off-by: Woraphat Khiaodaeng <worapat.kd2@gmail.com>
Link: https://patch.msgid.link/20260730162741.1125-1-worapat.kd2@gmail.com
Reviewed-by: Pavel Begunkov <asml.silence@gmail.com>
[axboe: add pavel edit]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
io_uring/zcrx.c

index 76b9b0d54af9e79541f4a25afbf2325a4c2731fd..f1464ea8ca64a4d1c2df852f8ef6ded8b86cf696 100644 (file)
@@ -808,7 +808,8 @@ err_xa_erase:
        scoped_guard(mutex, &ctx->mmap_lock)
                xa_erase(&ctx->zcrx_ctxs, id);
 err:
-       zcrx_unregister(ifq, ctx);
+       /* the import path never set the ->master_ctx ref, don't drop it */
+       zcrx_unregister(ifq, NULL);
        return ret;
 }