* [BUG] io_uring: 13 remaining unprotected ctx->rings accesses after 61a11cf481272
@ 2026-04-09 9:14 asaf meizner
2026-04-09 11:39 ` Jens Axboe
0 siblings, 1 reply; 2+ messages in thread
From: asaf meizner @ 2026-04-09 9:14 UTC (permalink / raw)
To: axboe; +Cc: io-uring, linux-kernel
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 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.
Minimal fix for fdinfo:
void io_uring_show_fdinfo(struct io_ring_ctx *ctx)
{
- struct io_rings *r = ctx->rings;
+ struct io_rings *r;
+ rcu_read_lock();
+ r = rcu_dereference(ctx->rings);
/* ... use r ... */
+ rcu_read_unlock();
}
I haven't written a full patch for all 13 sites because the right
fix depends on whether io_get_rings() or raw rcu_read_lock() is
preferred for each site, and some of the tw.c accesses may be
intentionally unprotected for !DEFER_TASKRUN. Happy to write the
full patch if you can clarify which sites actually need fixing.
Reproducer:
Thread A: cat /proc/$(pidof target)/fdinfo/$(target_uring_fd)
Thread B: io_uring_register(fd, IORING_REGISTER_RESIZE_RINGS, ...)
Tested against: current HEAD (7.0-rc series)
Thanks,
Asaf Meizner
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [BUG] io_uring: 13 remaining unprotected ctx->rings accesses after 61a11cf481272
2026-04-09 9:14 [BUG] io_uring: 13 remaining unprotected ctx->rings accesses after 61a11cf481272 asaf meizner
@ 2026-04-09 11:39 ` Jens Axboe
0 siblings, 0 replies; 2+ messages in thread
From: Jens Axboe @ 2026-04-09 11:39 UTC (permalink / raw)
To: asaf meizner; +Cc: io-uring, linux-kernel
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
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-09 11:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-09 9:14 [BUG] io_uring: 13 remaining unprotected ctx->rings accesses after 61a11cf481272 asaf meizner
2026-04-09 11:39 ` Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox