[BUG] fs/aio: io_setup integer wrap can exhaust global aio quota
From: Cheng Yongkang
Date: Thu Oct 22 2026 - 05:29:20 EST
Hi all,
ioctx_alloc() saves the 32-bit nr_events in max_reqs, then doubles
nr_events before checking the ring-size bound. For
nr_events=0x80000001, the doubled value wraps to 2, so the ring check
passes and aio_setup_ring() builds a one-page/127-slot ring. The
original 0x80000001 is still stored in ctx->max_reqs and charged to the
global aio_nr quota.
If an administrator has raised fs.aio-max-nr to at least 0x80000001,
an unprivileged process can create that context and make later normal
io_setup() calls fail with -EAGAIN until the wrapped context is
destroyed.
Observed on Linux v7.3, commit
08ab31164c55f20c7a2653de3186ae333470e2c1, with the administrator
raising fs.aio-max-nr to 2147483649 and dropping to uid/gid 65534:
io_setup(0x80000001): ret=0
fs.aio-nr: 2147483649
ring header nr: 127
ring VMA size: 4096
io_setup(1): -EAGAIN while wrapped context alive
io_destroy(): 0
fs.aio-nr after: 0
The unprivileged io_submit path stopped at the ring's real 127-slot
capacity.
The default fs.aio-max-nr=65536 blocks the path, so this requires the
documented but unusually large administrator configuration. Under that
configuration it is a system-wide availability failure.
Check the magnitude before doubling, or use a 64-bit intermediate and
charge the actual validated request count.
I found no matching upstream fix or CVE.
The privileged init wrapper and unprivileged PoC are available on
request.
This was found with a multi-agent workflow; the full
quota/ring sequence was reproduced in QEMU.
Thanks,
Cheng Yongkang