freenode
Kernel & Low-Level

NOMMU GUP pin path fixed after io_uring use-after-free

On kernels without an MMU, pin and unpin refcounts were asymmetric, so repeated io_uring fixed-buffer registration could free pages still mapped by userspace.

A reference-counting mismatch in the Linux kernel's get-user-pages path on no-MMU configurations could free pages that were still mapped, creating a use-after-free reachable through io_uring fixed buffers.

On builds without CONFIG_MMU, the pin path always took a plain single-page reference even when FOLL_PIN was set. The unpin helper is shared with MMU kernels and always subtracts the large FOLL_PIN bias (1024) from the folio refcount. Pin therefore added 1 while unpin subtracted 1024.

In practice a user could map a page, register it hundreds of times as an io_uring fixed buffer until the refcount reached 1024, then unregister. The first unpin drove the count to zero and returned the page to the buddy allocator. Later unpins wrote into whatever reclaimed that memory, and the original VMA still pointed at the freed page because NOMMU has no hardware to invalidate the mapping. If the page was reused for another io_uring structure, userspace could corrupt the new owner's data through the stale mapping.

Greg Kroah-Hartman fixed the NOMMU path so it applies the same pin bias MMU code already uses, making pin and unpin symmetric. David Hildenbrand (Arm) extended the change to keep supporting older callers that supply a pages array without FOLL_GET, and made the path return -EFAULT instead of leaving NULL entries in the output array. Anthropic reported the issue. The mismatch dates to the original FOLL_PIN tracking work.

Hildenbrand intends to carry the fix in the next merge window rather than as a stable hotfix, citing how long the bug has existed and the narrow NOMMU audience. Andrew Morton agreed with that plan.