Re: [BUG] io_uring: 13 remaining unprotected ctx->rings accesses after 61a11cf481272
From: Jens Axboe
Date: Thu Apr 09 2026 - 07:39:15 EST
On 4/9/26 3:14 AM, asaf meizner wrote:
> Hi Jens,
>
> Commit 61a11cf481272 ("io_uring: protect remaining lockless ctx->rings
> accesses with RCU") introduced RCU protection for ctx->rings during
> ring resize, but at least 13 dereferences were not converted. The
> most concerning is fdinfo.c:63 which reads ctx->rings with no lock
> and no RCU protection at all.
>
> Unprotected accesses found:
>
> io_uring/fdinfo.c:63
> struct io_rings *r = ctx->rings;
> (no lock, no RCU ? TOCTOU with concurrent resize)
>
> io_uring/tw.c:41, 253, 291, 326
> atomic_andnot/atomic_or on ctx->rings->sq_flags
> (lines 249-253 have a comment justifying no RCU for
> !DEFER_TASKRUN, but lines 41 and 291 have no such comment
> and appear to be in contexts where resize could race)
>
> io_uring/sqpoll.c:380, 407, 421
> atomic_or/atomic_andnot on ctx->rings->sq_flags
> (under sqd->lock, but resize takes uring_lock ? different locks)
>
> io_uring/io_uring.c:574, 647, 690, 1985, 1986
> CQ overflow flags and sq_dropped counter
>
> The fdinfo case is the clearest bug: a read of
> /proc/<pid>/fdinfo/<fd> concurrent with
> IORING_REGISTER_RESIZE_RINGS can dereference a stale ctx->rings
> pointer after the old rings are freed via RCU. This is a UAF read
> that could leak kernel heap data.
The "clearest bug" isn't a bug at all. What you are seemingly missing
here is that rings can't go away if inside ->uring_lock OR
->completion_lock, which is actually documented in io_get_rings().
> The sqpoll case is also concerning because sqd->lock and uring_lock
> are different locks, so the SQPOLL thread can see a stale pointer
> during resize.
And you are also missing that ring resizing isn't supported for SQPOLL,
it's a DEFER_TASKRUN only feature.
--
Jens Axboe