Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers
@ 2026-11-05  6:14 Henry Martin
  2026-11-05 13:28 ` Jens Axboe
  0 siblings, 1 reply; 4+ messages in thread
From: Henry Martin @ 2026-11-05  6:14 UTC (permalink / raw)
  To: axboe; +Cc: io-uring, stable, Henry Martin

Legacy provided buffers store a raw user virtual address.  It is
recorded in io_add_buffers() and later returned verbatim by
io_provided_buffer_select().  The consumer (recv, send, read)
imports it as a user pointer, so it is resolved in the address
space of whichever task issues the request.  Nothing ties a buffer
to the address space it was provided from.

When a ring is shared between tasks with different address spaces,
for example across fork(2) and execve(2), a consuming request reads
from or writes to an address chosen by the provider, in the
consumer's address space.

Record the providing task's mm in each buffer, and refuse selection
when the consuming task runs under a different mm.  The buffer stays
queued for its owner and the request fails with -ENOBUFS.  The mm
reference is held while the buffer is queued.

This issue was discovered by Tencent CodeBuddy Security.

Reported-by: zeroday zhao <zerodayzhao@tencent.com>
Fixes: ddf0322db79c5 ("io_uring: add IORING_OP_PROVIDE_BUFFERS")
Cc: stable@vger.kernel.org
Signed-off-by: Henry Martin <bsdhenrymartin@gmail.com>
---
 io_uring/kbuf.c | 23 ++++++++++++++++++++---
 io_uring/kbuf.h |  2 ++
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index abf44f5dba26a..b585105a17202 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -4,6 +4,7 @@
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/mm.h>
+#include <linux/sched/mm.h>
 #include <linux/slab.h>
 #include <linux/namei.h>
 #include <linux/poll.h>
@@ -97,12 +98,18 @@ static int io_buffer_add_list(struct io_ring_ctx *ctx,
 	return xa_err(xa_store(&ctx->io_bl_xa, bgid, bl, GFP_KERNEL));
 }
 
+static void io_kbuf_free(struct io_buffer *buf)
+{
+	mmdrop(buf->mm);
+	kfree(buf);
+}
+
 void io_kbuf_drop_legacy(struct io_kiocb *req)
 {
 	if (WARN_ON_ONCE(!(req->flags & REQ_F_BUFFER_SELECTED)))
 		return;
 	req->flags &= ~REQ_F_BUFFER_SELECTED;
-	kfree(req->kbuf);
+	io_kbuf_free(req->kbuf);
 	req->kbuf = NULL;
 }
 
@@ -124,7 +131,7 @@ bool io_kbuf_recycle_legacy(struct io_kiocb *req, unsigned issue_flags)
 		list_add(&buf->list, &bl->buf_list);
 		bl->nbufs++;
 	} else {
-		kfree(buf);
+		io_kbuf_free(buf);
 	}
 	req->flags &= ~REQ_F_BUFFER_SELECTED;
 	req->kbuf = NULL;
@@ -140,6 +147,14 @@ static void __user *io_provided_buffer_select(struct io_kiocb *req, size_t *len,
 		struct io_buffer *kbuf;
 
 		kbuf = list_first_entry(&bl->buf_list, struct io_buffer, list);
+		/*
+		 * The buffer address is a user virtual address that gets
+		 * interpreted in the address space of whoever issues the
+		 * request. Refuse consumption by a different mm than the
+		 * one that provided the buffer.
+		 */
+		if (unlikely(kbuf->mm != current->mm))
+			return NULL;
 		list_del(&kbuf->list);
 		bl->nbufs--;
 		if (*len == 0 || *len > kbuf->len)
@@ -452,7 +467,7 @@ static int io_remove_buffers_legacy(struct io_ring_ctx *ctx,
 		nxt = list_first_entry(&bl->buf_list, struct io_buffer, list);
 		list_del(&nxt->list);
 		bl->nbufs--;
-		kfree(nxt);
+		io_kbuf_free(nxt);
 		cond_resched();
 	}
 	return i;
@@ -575,6 +590,8 @@ static int io_add_buffers(struct io_ring_ctx *ctx, struct io_provide_buf *pbuf,
 		buf->len = min_t(__u32, pbuf->len, MAX_RW_COUNT);
 		buf->bid = bid;
 		buf->bgid = pbuf->bgid;
+		buf->mm = current->mm;
+		mmgrab(buf->mm);
 		addr += pbuf->len;
 		bid++;
 		cond_resched();
diff --git a/io_uring/kbuf.h b/io_uring/kbuf.h
index 401773e1ef805..fe0b01c475a49 100644
--- a/io_uring/kbuf.h
+++ b/io_uring/kbuf.h
@@ -44,6 +44,8 @@ struct io_buffer_list {
 
 struct io_buffer {
 	struct list_head list;
+	/* mm of the task that provided this buffer, see io_add_buffers() */
+	struct mm_struct *mm;
 	__u64 addr;
 	__u32 len;
 	__u16 bid;
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers
  2026-11-05  6:14 [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers Henry Martin
@ 2026-11-05 13:28 ` Jens Axboe
  2026-11-06 17:31   ` henry martin
  0 siblings, 1 reply; 4+ messages in thread
From: Jens Axboe @ 2026-11-05 13:28 UTC (permalink / raw)
  To: Henry Martin; +Cc: io-uring, stable

On 11/4/26 11:14 PM, Henry Martin wrote:
> Legacy provided buffers store a raw user virtual address.  It is
> recorded in io_add_buffers() and later returned verbatim by
> io_provided_buffer_select().  The consumer (recv, send, read)
> imports it as a user pointer, so it is resolved in the address
> space of whichever task issues the request.  Nothing ties a buffer
> to the address space it was provided from.
> 
> When a ring is shared between tasks with different address spaces,
> for example across fork(2) and execve(2), a consuming request reads
> from or writes to an address chosen by the provider, in the
> consumer's address space.
> 
> Record the providing task's mm in each buffer, and refuse selection
> when the consuming task runs under a different mm.  The buffer stays
> queued for its owner and the request fails with -ENOBUFS.  The mm
> reference is held while the buffer is queued.
> 
> This issue was discovered by Tencent CodeBuddy Security.

This is nonsense.

This isn't a bug. A ring is shared memory, and anyone with access to it
can write SQEs that the other side submits, with whatever addresses they
want. Provided buffers are no different from the addr field in any SQE,
and the same holds for ring provided buffers which this patch doesn't
even touch. Sharing a ring with another address space means fully
trusting it, that's inherent to the design.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers
  2026-11-05 13:28 ` Jens Axboe
@ 2026-11-06 17:31   ` henry martin
  2026-11-06 17:34     ` Jens Axboe
  0 siblings, 1 reply; 4+ messages in thread
From: henry martin @ 2026-11-06 17:31 UTC (permalink / raw)
  To: Jens Axboe; +Cc: io-uring, stable

On 11/5/26, Jens Axboe wrote:
> This isn't a bug. A ring is shared memory, and anyone with access to it
> can write SQEs that the other side submits, with whatever addresses they
> want. Provided buffers are no different from the addr field in any SQE,
> and the same holds for ring provided buffers which this patch doesn't
> even touch. Sharing a ring with another address space means fully
> trusting it, that's inherent to the design.

Agreed — the SQE address fields already grant that capability, and
ring provided buffers have the same property, so this adds nothing
beyond what sharing a ring already means.

Thanks for the review,
Henry

Jens Axboe <axboe@kernel.dk> 于2026年11月5日周四 21:28写道:
>
> On 11/4/26 11:14 PM, Henry Martin wrote:
> > Legacy provided buffers store a raw user virtual address.  It is
> > recorded in io_add_buffers() and later returned verbatim by
> > io_provided_buffer_select().  The consumer (recv, send, read)
> > imports it as a user pointer, so it is resolved in the address
> > space of whichever task issues the request.  Nothing ties a buffer
> > to the address space it was provided from.
> >
> > When a ring is shared between tasks with different address spaces,
> > for example across fork(2) and execve(2), a consuming request reads
> > from or writes to an address chosen by the provider, in the
> > consumer's address space.
> >
> > Record the providing task's mm in each buffer, and refuse selection
> > when the consuming task runs under a different mm.  The buffer stays
> > queued for its owner and the request fails with -ENOBUFS.  The mm
> > reference is held while the buffer is queued.
> >
> > This issue was discovered by Tencent CodeBuddy Security.
>
> This is nonsense.
>
> This isn't a bug. A ring is shared memory, and anyone with access to it
> can write SQEs that the other side submits, with whatever addresses they
> want. Provided buffers are no different from the addr field in any SQE,
> and the same holds for ring provided buffers which this patch doesn't
> even touch. Sharing a ring with another address space means fully
> trusting it, that's inherent to the design.
>
> --
> Jens Axboe
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers
  2026-11-06 17:31   ` henry martin
@ 2026-11-06 17:34     ` Jens Axboe
  0 siblings, 0 replies; 4+ messages in thread
From: Jens Axboe @ 2026-11-06 17:34 UTC (permalink / raw)
  To: henry martin; +Cc: io-uring, stable

On 11/6/26 10:31 AM, henry martin wrote:
> On 11/5/26, Jens Axboe wrote:
>> This isn't a bug. A ring is shared memory, and anyone with access to it
>> can write SQEs that the other side submits, with whatever addresses they
>> want. Provided buffers are no different from the addr field in any SQE,
>> and the same holds for ring provided buffers which this patch doesn't
>> even touch. Sharing a ring with another address space means fully
>> trusting it, that's inherent to the design.
> 
> Agreed ? the SQE address fields already grant that capability, and
> ring provided buffers have the same property, so this adds nothing
> beyond what sharing a ring already means.

Forget SQEs or whatever, what stops you from inputting a virtual address
from another process into a read(2) system call you're doing? This has
nothing to do with io_uring AT ALL, and everything to do with people
using LLMs to try and find things and not even considering what those
"findings" are before sending them out and wasting other peoples time...

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-06 17:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05  6:14 [PATCH] io_uring/kbuf: fix cross-mm interpretation of provided buffers Henry Martin
2026-11-05 13:28 ` Jens Axboe
2026-11-06 17:31   ` henry martin
2026-11-06 17:34     ` Jens Axboe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox