* [PATCH] io_uring/kbuf: free the old cached iovec, not the returned one, on bundle grow
@ 2026-07-13 18:31 Doruk Tan Ozturk
2026-07-14 14:02 ` Gabriel Krisman Bertazi
0 siblings, 1 reply; 3+ messages in thread
From: Doruk Tan Ozturk @ 2026-07-13 18:31 UTC (permalink / raw)
To: axboe; +Cc: io-uring, Doruk Tan Ozturk
Commit cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer
bundle grow failure") moved the KBUF_MODE_FREE kfree() out of the expand
branch to after the validation loop, so the old cached iovec is only
released once the new buffers have been validated. However, by the time
control reaches the post-loop free, arg->iovs has already been reassigned
in the expand branch to the freshly allocated array that is about to be
returned to the caller:
iov = kmalloc_objs(struct iovec, nr_avail);
...
arg->iovs = iov; /* now the array we return */
...
if (arg->mode & KBUF_MODE_FREE)
kfree(arg->iovs); /* ... but this frees it */
On a successful grow, io_ring_buffers_peek() therefore frees the very
iovec array it returns. io_recv_buf_select() then builds an iov_iter over
that freed array and caches it in kmsg->vec.iovec, giving a
slab-use-after-free read during the recv copy and a later double free of
the iovec array on request cleanup. The array is a kmalloc() whose size is
controlled by the number of ring buffers the caller commits, so the freed
object lands in an attacker-influenced kmalloc cache.
KBUF_MODE_FREE is meant to release the *old* cached iovec once it has been
replaced by a larger one. Free the captured org_iovs instead, and only
when a grow actually happened (arg->iovs != org_iovs) so the no-grow case
still returns the reused array. The -EFAULT failure path already frees the
new array and leaves org_iovs for the caller, so it is unaffected.
Reproduced on next-20260710 with KASAN by an unprivileged IORING_OP_RECV
using IORING_RECVSEND_BUNDLE over a provided-buffer ring: a first
(expanding) bundle caches a small iovec, and an in-request bundle retry
grows again under KBUF_MODE_FREE, triggering both the UAF read and the
double free. The change eliminates the KASAN splat.
Fixes: cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer bundle grow failure")
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
io_uring/kbuf.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index b6b969b55e12..07d81dc7cbe2 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -328,8 +328,8 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
buf = io_ring_head_to_buf(br, ++head, bl->mask);
} while (--nr_iovs);
- if (arg->mode & KBUF_MODE_FREE)
- kfree(arg->iovs);
+ if ((arg->mode & KBUF_MODE_FREE) && arg->iovs != org_iovs)
+ kfree(org_iovs);
if (head == tail)
req->flags |= REQ_F_BL_EMPTY;
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] io_uring/kbuf: free the old cached iovec, not the returned one, on bundle grow
2026-07-13 18:31 [PATCH] io_uring/kbuf: free the old cached iovec, not the returned one, on bundle grow Doruk Tan Ozturk
@ 2026-07-14 14:02 ` Gabriel Krisman Bertazi
0 siblings, 0 replies; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2026-07-14 14:02 UTC (permalink / raw)
To: Doruk Tan Ozturk, axboe; +Cc: io-uring, Doruk Tan Ozturk
Doruk Tan Ozturk <doruk@0sec.ai> writes:
> Commit cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer
> bundle grow failure") moved the KBUF_MODE_FREE kfree() out of the expand
> branch to after the validation loop, so the old cached iovec is only
> released once the new buffers have been validated. However, by the time
> control reaches the post-loop free, arg->iovs has already been reassigned
> in the expand branch to the freshly allocated array that is about to be
> returned to the caller:
>
> iov = kmalloc_objs(struct iovec, nr_avail);
> ...
> arg->iovs = iov; /* now the array we return */
> ...
> if (arg->mode & KBUF_MODE_FREE)
> kfree(arg->iovs); /* ... but this frees it */
>
> On a successful grow, io_ring_buffers_peek() therefore frees the very
> iovec array it returns. io_recv_buf_select() then builds an iov_iter over
> that freed array and caches it in kmsg->vec.iovec, giving a
> slab-use-after-free read during the recv copy and a later double free of
> the iovec array on request cleanup. The array is a kmalloc() whose size is
> controlled by the number of ring buffers the caller commits, so the freed
> object lands in an attacker-influenced kmalloc cache.
>
> KBUF_MODE_FREE is meant to release the *old* cached iovec once it has been
> replaced by a larger one. Free the captured org_iovs instead, and only
> when a grow actually happened (arg->iovs != org_iovs) so the no-grow case
> still returns the reused array. The -EFAULT failure path already frees the
> new array and leaves org_iovs for the caller, so it is unaffected.
>
> Reproduced on next-20260710 with KASAN by an unprivileged IORING_OP_RECV
> using IORING_RECVSEND_BUNDLE over a provided-buffer ring: a first
> (expanding) bundle caches a small iovec, and an in-request bundle retry
> grows again under KBUF_MODE_FREE, triggering both the UAF read and the
> double free. The change eliminates the KASAN splat.
>
> Fixes: cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer bundle grow failure")
> Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
Already fixed here
https://lore.kernel.org/io-uring/20260712142612.188695595-iostreampy@proton.me/T/#u
here:
https://lore.kernel.org/io-uring/OS3PR01MB8810F38D613E37FBD684DC4D83FB2@OS3PR01MB8810.jpnprd01.prod.outlook.com/T/#t
and here:
https://lore.kernel.org/io-uring/20260713-io_uring_dangling-v1-1-b9bdc0f0e776@debian.org/T/#u
Aren't LLMs fun?
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] io_uring/kbuf: free the old cached iovec, not the returned one, on bundle grow
@ 2026-07-14 14:14 Doruk Tan Ozturk
0 siblings, 0 replies; 3+ messages in thread
From: Doruk Tan Ozturk @ 2026-07-14 14:14 UTC (permalink / raw)
To: krisman; +Cc: axboe, io-uring
> Aren't LLMs fun?
Ha — they are indeed. Beat me to it by a day. Please drop mine.
Best,
Doruk
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-14 14:14 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 18:31 [PATCH] io_uring/kbuf: free the old cached iovec, not the returned one, on bundle grow Doruk Tan Ozturk
2026-07-14 14:02 ` Gabriel Krisman Bertazi
-- strict thread matches above, loose matches on Subject: below --
2026-07-14 14:14 Doruk Tan Ozturk
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox