[PATCH] io_uring/kbuf: free cached iovec only after replacement

Junye Ji via B4 Relay posted 1 patch 1 week, 6 days ago
io_uring/kbuf.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
[PATCH] io_uring/kbuf: free cached iovec only after replacement
Posted by Junye Ji via B4 Relay 1 week, 6 days ago
From: Junye Ji <jijunye1@outlook.com>

io_ring_buffers_peek() saves the incoming iovec array in org_iovs, then may
replace arg->iovs while expanding a provided-buffer bundle. With
KBUF_MODE_FREE set, the success path frees arg->iovs instead of org_iovs.
It either frees a replacement before the caller uses it or, when no
replacement was needed, frees the cache still owned by the request. Cleanup
later frees the same cache again.

I reproduced the stale read and double free on the no-growth and
successful-growth paths with two completions from one multishot receive.
The fixed KASAN kernel completed 100 runs of both triggers and both
liburing bundle tests without a report.

Fixes: cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer bundle grow failure")
Assisted-by: Codex-Security:unspecified
Signed-off-by: Junye Ji <jijunye1@outlook.com>
---
I can send the reproducer and full trace/KASAN logs privately if needed.
---
 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;

---
base-commit: 44696aa3a489d2baf58efa61b37833f100072bee
change-id: 20260712-io-uring-kbuf-iovec-lifetime-4ff406a0b0c3

Best regards,
-- 
Junye Ji <jijunye1@outlook.com>