Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable
@ 2026-11-04 18:44 Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 01/16] io_uring/kbuf: advance head when a pickup empties the buffer ring Jens Axboe
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

Hi Greg,

Here are io_uring fixes for 7.3-stable.

They apply on top of v7.3.1 with the current queue-7.3 applied
(stable-queue 6575ff04f2fd). They depend on the io_uring patches that
are already in queue-7.3: from patch 14 onwards they won't apply to
v7.3.1 alone.

The other io_uring fixes I had for 7.3 that are already in queue-7.3
aren't included here.

Cheng Yongkang (2):
  io_uring/timeout: count cancelled timeouts in cq_timeouts
  io_uring/net: fix multishot recv length limit accounting

Chris Mason (6):
  io_uring/rsrc: charge buffer arrays and nodes to memcg
  io_uring: charge tctx nodes and personalities
  io_uring: free XArray nodes left by a failed store
  io_uring/bpf-ops: clear bpf_ctx while submitting SQEs
  io_uring: free the task context after a grace period
  io_uring/sqpoll: don't wait for the thread to exit

Daan De Meyer (1):
  io_uring/net: don't retry a non-blocking connect() that fails with
    -EAGAIN

Dominik Maier (2):
  io_uring/net: mask socket type with SOCK_TYPE_MASK in
    io_socket_bpf_populate()
  io_uring/register: enforce per-task restrictions in
    io_uring_register_blind()

Jens Axboe (1):
  io_uring: keep the drain active when a deferred drain request is
    started

Josef Bacik (1):
  io_uring/io-wq: no new workers once the exit has begun

Kyle Zeng (1):
  io_uring: defer foreign io-wq enqueues to task_work

Yichong Chen (2):
  io_uring/kbuf: advance head when a pickup empties the buffer ring
  io_uring/net: keep recvmsg buffer select length within MAX_RW_COUNT

 include/linux/io_uring_types.h |  3 ++
 io_uring/bpf-ops.c             | 16 ++++++++++-
 io_uring/io-wq.c               |  6 ++++
 io_uring/io_uring.c            | 50 +++++++++++++++++++++++++++-------
 io_uring/kbuf.c                |  4 ++-
 io_uring/net.c                 | 25 +++++++++++++----
 io_uring/register.c            |  7 +++++
 io_uring/rsrc.c                |  7 +++--
 io_uring/sqpoll.c              | 28 +++++++++++++++----
 io_uring/sqpoll.h              |  3 +-
 io_uring/tctx.c                |  9 ++++--
 io_uring/timeout.c             |  3 ++
 12 files changed, 132 insertions(+), 29 deletions(-)

-- 
2.56.0


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

* [PATCH 7.3.y 01/16] io_uring/kbuf: advance head when a pickup empties the buffer ring
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 02/16] io_uring/net: keep recvmsg buffer select length within MAX_RW_COUNT Jens Axboe
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Yichong Chen <chenyichong@uniontech.com>

Commit 8742f4b5a472c347fe568a73468a2d9e4d630b33 upstream.

io_ring_buffers_peek() flags the request with REQ_F_BL_EMPTY if the
buffers it picked were the last ones available, comparing head to tail.
When the loop breaks because max_len was exhausted the buffer was picked,
but head is left pointing at it, so the flag isn't set if it was the last
buffer. A bundle receive then retries, finds no buffer, and the request,
which has received data, is marked as failed on account of the -ENOBUFS.
That severs a link, and ends a multishot.

Fixes: 35c8711c8fc4 ("io_uring/kbuf: add helpers for getting/peeking multiple buffers")
Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
Link: https://patch.msgid.link/20261023091711.83481-1-chenyichong@uniontech.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/kbuf.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index c9641b6ccc8f..abf44f5dba26 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -326,8 +326,10 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
 
 		arg->out_len += len;
 		arg->max_len -= len;
-		if (!arg->max_len)
+		if (!arg->max_len) {
+			head++;
 			break;
+		}
 
 		buf = io_ring_head_to_buf(br, ++head, bl->mask);
 	} while (--nr_iovs);
-- 
2.56.0


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

* [PATCH 7.3.y 02/16] io_uring/net: keep recvmsg buffer select length within MAX_RW_COUNT
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 01/16] io_uring/kbuf: advance head when a pickup empties the buffer ring Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 03/16] io_uring/net: mask socket type with SOCK_TYPE_MASK in io_socket_bpf_populate() Jens Axboe
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Yichong Chen <chenyichong@uniontech.com>

Commit 18d3f56eb6e6500a65d81cf265f0fe74fd9ecab6 upstream.

io_recvmsg() takes the transfer length from the ring provided buffer when
sqe->len is 0, and such a buffer can be up to 4G. min_ret is an int, so 2G
or more makes it negative and the (ret < min_ret) block is skipped, and the
oversized length reaches the socket layer unchanged: an empty socket then
completes with 0 instead of arming poll, and a short read is reported as
complete. Clamp the length to MAX_RW_COUNT, as io_recv() does.

Fixes: c7fb19428d67 ("io_uring: add support for ring mapped supplied buffers")
Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
Link: https://patch.msgid.link/20261023112209.87979-1-chenyichong@uniontech.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/net.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/io_uring/net.c b/io_uring/net.c
index eeab88e77fd8..4678f29f5177 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -1068,6 +1068,7 @@ int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags)
 		sel = io_buffer_select(req, &len, sr->buf_group, issue_flags);
 		if (!sel.addr)
 			return -ENOBUFS;
+		len = min_t(size_t, len, MAX_RW_COUNT);
 
 		if (req->flags & REQ_F_APOLL_MULTISHOT) {
 			ret = io_recvmsg_prep_multishot(kmsg, sr, &sel.addr, &len);
-- 
2.56.0


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

* [PATCH 7.3.y 03/16] io_uring/net: mask socket type with SOCK_TYPE_MASK in io_socket_bpf_populate()
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 01/16] io_uring/kbuf: advance head when a pickup empties the buffer ring Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 02/16] io_uring/net: keep recvmsg buffer select length within MAX_RW_COUNT Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 04/16] io_uring/register: enforce per-task restrictions in io_uring_register_blind() Jens Axboe
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Dominik Maier <dmnk@google.com>

Commit e66907c3ba229399ecfb4ef032b95a4a071c894e upstream.

When an IORING_OP_SOCKET request is prepared in io_socket_prep(),
sock->type retains creation flags such as SOCK_CLOEXEC and SOCK_NONBLOCK
until __sys_socket_file() masks them off with SOCK_TYPE_MASK during
execution.

However, io_socket_bpf_populate() copies sock->type into
bctx->socket.type without masking off ~SOCK_TYPE_MASK. As a result, a
cBPF filter inspecting bctx->socket.type against socket types such as
SOCK_DGRAM or SOCK_STREAM can be bypassed by ORing SOCK_CLOEXEC or
SOCK_NONBLOCK into the socket type.

Mask sock->type with SOCK_TYPE_MASK when populating bctx->socket.type.

Fixes: cff1c26b4223 ("io_uring/net: allow filtering on IORING_OP_SOCKET data")
Assisted-by: ARTIST LLM
Signed-off-by: Dominik Maier <dmnk@google.com>
Link: https://patch.msgid.link/20261022232120.2537530-1-dmnk@google.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/net.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/io_uring/net.c b/io_uring/net.c
index 4678f29f5177..ef455f1a47eb 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -1734,7 +1734,7 @@ void io_socket_bpf_populate(struct io_uring_bpf_ctx *bctx, struct io_kiocb *req)
 	struct io_socket *sock = io_kiocb_to_cmd(req, struct io_socket);
 
 	bctx->socket.family = sock->domain;
-	bctx->socket.type = sock->type;
+	bctx->socket.type = sock->type & SOCK_TYPE_MASK;
 	bctx->socket.protocol = sock->protocol;
 }
 
-- 
2.56.0


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

* [PATCH 7.3.y 04/16] io_uring/register: enforce per-task restrictions in io_uring_register_blind()
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (2 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 03/16] io_uring/net: mask socket type with SOCK_TYPE_MASK in io_socket_bpf_populate() Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 05/16] io_uring/net: don't retry a non-blocking connect() that fails with -EAGAIN Jens Axboe
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Dominik Maier <dmnk@google.com>

Commit 4a93427e99a8524f3612f29ec8bc0da5f8435a51 upstream.

When io_uring_register(2) is invoked with fd == -1, it dispatches
directly to io_uring_register_blind() without checking
current->io_uring_restrict.

As a result, a task with per-task register restrictions installed via
IORING_REGISTER_RESTRICTIONS can still invoke blind registration opcodes
such as IORING_REGISTER_SEND_MSG_RING even when those opcodes are not in
the task's allowed register_op bitmap.

Check current->io_uring_restrict->register_op in
io_uring_register_blind() before dispatching blind registration opcodes.

Fixes: ed82f35b926b ("io_uring: allow registration of per-task restrictions")
Assisted-by: ARTIST LLM
Signed-off-by: Dominik Maier <dmnk@google.com>
Link: https://patch.msgid.link/20261022232121.2538616-1-dmnk@google.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/register.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/io_uring/register.c b/io_uring/register.c
index 2ce36623e25a..62f492e6472f 100644
--- a/io_uring/register.c
+++ b/io_uring/register.c
@@ -1058,6 +1058,13 @@ static int io_uring_register_send_msg_ring(void __user *arg, unsigned int nr_arg
 static int io_uring_register_blind(unsigned int opcode, void __user *arg,
 				   unsigned int nr_args)
 {
+	if (current->io_uring_restrict &&
+	    current->io_uring_restrict->reg_registered) {
+		opcode = array_index_nospec(opcode, IORING_REGISTER_LAST);
+		if (!test_bit(opcode, current->io_uring_restrict->register_op))
+			return -EACCES;
+	}
+
 	switch (opcode) {
 	case IORING_REGISTER_SEND_MSG_RING:
 		return io_uring_register_send_msg_ring(arg, nr_args);
-- 
2.56.0


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

* [PATCH 7.3.y 05/16] io_uring/net: don't retry a non-blocking connect() that fails with -EAGAIN
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (3 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 04/16] io_uring/register: enforce per-task restrictions in io_uring_register_blind() Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 06/16] io_uring: keep the drain active when a deferred drain request is started Jens Axboe
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Daan De Meyer <daan@amutable.com>

Commit 71537ed811107c514c0342e98902d489d6d2b7cd upstream.

IORING_OP_CONNECT on an O_NONBLOCK AF_UNIX stream socket retries a
connect that fails with -EAGAIN because the listener's backlog is full.
Polling the client socket doesn't help: an unconnected stream socket
reports EPOLLOUT and EPOLLHUP right away, before the backlog drains. So
io_uring retries the connect until the apoll retry limit is hit, then
punts the request to io-wq. With a full backlog, io_connect() runs 128
times in the submitting task and once more in a new io-wq worker. That
worker stays in the task's thread group, so a later
unshare(CLONE_NEWUSER) fails with -EINVAL.

None of this changes the result. __sys_connect_file() adds the socket's
own O_NONBLOCK to every attempt, so the attempt from io-wq fails with
-EAGAIN as well. That -EAGAIN becomes the CQE result, and connect(2)
returns the same -EAGAIN.

Let's complete the request with -EAGAIN right away when the socket has
O_NONBLOCK set, like connect(2) does. io_accept() already completes
-EAGAIN this way for IORING_ACCEPT_DONTWAIT. -EINPROGRESS still waits
for the connection to complete. A blocking socket still ends up in
io-wq, where the connect sleeps until the listener accepts.

Fixes: f8e85cf255ad ("io_uring: add support for IORING_OP_CONNECT")
Signed-off-by: Daan De Meyer <daan@amutable.com>
Link: https://patch.msgid.link/20261027-b4-io-uring-connect-eagain-v1-1-f4e3aaaa49ee@amutable.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/net.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/io_uring/net.c b/io_uring/net.c
index ef455f1a47eb..b62f21df42f2 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -1878,6 +1878,17 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
 			if (connect->seen_econnaborted)
 				goto out;
 			connect->seen_econnaborted = true;
+		} else if (req->file->f_flags & O_NONBLOCK) {
+			/*
+			 * __sys_connect_file() adds O_NONBLOCK from the socket
+			 * to every attempt, so a retry from io-wq fails with
+			 * -EAGAIN as well. A poll on the socket doesn't wait
+			 * until a retry can succeed. For example, an AF_UNIX
+			 * client reports EPOLLOUT right away while the
+			 * listener's backlog is full. Complete the request
+			 * with -EAGAIN, like connect(2) does.
+			 */
+			goto out;
 		}
 		return -EAGAIN;
 	}
-- 
2.56.0


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

* [PATCH 7.3.y 06/16] io_uring: keep the drain active when a deferred drain request is started
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (4 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 05/16] io_uring/net: don't retry a non-blocking connect() that fails with -EAGAIN Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 07/16] io_uring/io-wq: no new workers once the exit has begun Jens Axboe
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

Commit 2295613cd3713beffc8665e956135a5d48dfc208 upstream.

ecb6817b769f ends the drain when the dispatched drain request has posted
its CQE and nothing else is deferred. But the deferred list is processed
in between, and if the request it starts is itself a drain request, the
drain is over before that one has even run. A request submitted after
that point then runs ahead of it:

  1. P0 in flight, D1 and D2 (both IOSQE_IO_DRAIN) deferred behind it
  2. P0 completes, D1 is started
  3. D1 completes, drain_done is set, D2 is started, the list is empty
  4. IO_RING_F_DRAIN_ACTIVE is cleared with D2 still in flight

Anything submitted now bypasses the drain. The same happens with D2 as
the head of a link, as those aren't tracked in ->drain_req at all.

Have io_queue_deferred() report whether it started a drain request, and
don't end the drain if it did. Starting non-drain requests and ending it
is still fine, they were only waiting on the drain request that just
completed.

Fixes: ecb6817b769f ("io_uring: end the drain when the drain request completes")
Reported-by: Karl Mehltretter <kmehltretter@gmail.com>
Link: https://lore.kernel.org/io-uring/20261027043323.98916-1-kmehltretter@gmail.com/
Cc: stable@vger.kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/io_uring.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 3d715536df72..dd6ad02d0e89 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -474,9 +474,9 @@ unsigned io_linked_nr(struct io_kiocb *req)
 	return nr;
 }
 
-static __cold noinline void io_queue_deferred(struct io_ring_ctx *ctx)
+static __cold noinline bool io_queue_deferred(struct io_ring_ctx *ctx)
 {
-	bool drain_seen = false, first = true;
+	bool drain_seen = false, first = true, drain_queued = false;
 
 	lockdep_assert_held(&ctx->uring_lock);
 	__io_req_caches_free(ctx);
@@ -490,17 +490,21 @@ static __cold noinline void io_queue_deferred(struct io_ring_ctx *ctx)
 
 		drain_seen |= de->req->flags & REQ_F_IO_DRAIN;
 		if ((drain_seen || first) && ctx->nr_req_allocated != ctx->nr_drained)
-			return;
+			return drain_queued;
 
 		list_del_init(&de->list);
 		ctx->nr_drained -= io_linked_nr(de->req);
-		/* it runs alone, track its completion unless it's a link */
-		if ((de->req->flags & REQ_F_IO_DRAIN) && !de->req->link)
-			ctx->drain_req = de->req;
+		if (de->req->flags & REQ_F_IO_DRAIN) {
+			drain_queued = true;
+			/* it runs alone, track its completion unless it's a link */
+			if (!de->req->link)
+				ctx->drain_req = de->req;
+		}
 		io_req_task_queue(de->req);
 		kfree(de);
 		first = false;
 	}
+	return drain_queued;
 }
 
 void __io_commit_cqring_flush(struct io_ring_ctx *ctx)
@@ -1227,7 +1231,9 @@ void __io_submit_flush_completions(struct io_ring_ctx *ctx)
 	}
 
 	if (unlikely(ctx->int_flags & IO_RING_F_DRAIN_ACTIVE)) {
-		io_queue_deferred(ctx);
+		/* a drain request started from the list keeps the drain going */
+		if (io_queue_deferred(ctx))
+			drain_done = false;
 		if (drain_done && list_empty(&ctx->defer_list))
 			ctx->int_flags &= ~IO_RING_F_DRAIN_ACTIVE;
 	}
-- 
2.56.0


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

* [PATCH 7.3.y 07/16] io_uring/io-wq: no new workers once the exit has begun
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (5 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 06/16] io_uring: keep the drain active when a deferred drain request is started Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 08/16] io_uring/rsrc: charge buffer arrays and nodes to memcg Jens Axboe
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Josef Bacik <josef@toxicpanda.com>

Commit 47c68f925062a6013235d06add85a59f7d91e0fb upstream.

io_wq_exit_start() sets IO_WQ_BIT_EXIT, but worker creation task_work
queued before that stays queued until io_wq_exit_workers() cancels it.
In between the task runs its task_work while canceling requests, and a
worker can still get created.

That's a problem for exec. begin_new_exec() calls io_uring_task_cancel()
after de_thread(), when it's supposedly the only thread left, and
io_wq_exit_workers() only waits for ->worker_done, which a worker
completes before do_exit(). So the exiting worker is still in the thread
group, using the mm, files and sighand, while exec replaces them. Easy
to hit with a signal pending when create_io_thread() fails and the retry
gets queued right before execve().

Cancel the queued creation task_work as soon as the exit bit is set.
Pending work that isn't otherwise canceled now ends with -ECANCELED when
the io-wq is destroyed, rather than depending on whether the task
happened to run its task_work first.

Fixes: 685fe7feedb9 ("io-wq: eliminate the need for a manager thread")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[axboe: the barrier is already upstream, see 0e5663856b69, only the
 cancel is new]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/io-wq.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index c48f2475d03c..6b9395060262 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -1416,6 +1416,12 @@ void io_wq_exit_start(struct io_wq *wq)
 	set_bit(IO_WQ_BIT_EXIT, &wq->state);
 	/* Pairs with task_work_add() in io_queue_worker_create(). */
 	smp_mb__after_atomic();
+	/*
+	 * No more workers from here on. The caller is going to run its task
+	 * work, and on exec a thread that is created now would still be
+	 * around, exiting, by the time that the caller must be on its own.
+	 */
+	io_wq_cancel_tw_create(wq);
 }
 
 static void io_wq_cancel_tw_create(struct io_wq *wq)
-- 
2.56.0


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

* [PATCH 7.3.y 08/16] io_uring/rsrc: charge buffer arrays and nodes to memcg
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (6 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 07/16] io_uring/io-wq: no new workers once the exit has begun Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 09/16] io_uring: charge tctx nodes and personalities Jens Axboe
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 075d5837bafc317e796548f721b8fbf7add6821d upstream.

The io_mapped_ubuf of a registered buffer has one bio_vec per page,
unless the pages coalesce into huge pages. Huge pages only count against
RLIMIT_MEMLOCK once per ring, so a buffer that maps the same 2MB folio
over and over in a way that defeats coalescing gets a full bvec array
per registration while only the one folio counts as locked. 1GB of that
registered in 100 slots took several times a 64MB memory.max, with 2.6MB
charged.

Allocate the io_mapped_ubuf and the io_rsrc_node with
GFP_KERNEL_ACCOUNT. An object reused from the ring caches stays charged
to whoever allocated it first, same as the io_kiocb cache.

Fixes: de2939388be5 ("io_uring: improve registered buffer accounting for huge pages")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/rsrc.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index 51b46e624ddd..d69baa47c7e7 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -162,8 +162,9 @@ static struct io_mapped_ubuf *io_alloc_imu(struct io_ring_ctx *ctx,
 					   int nr_bvecs)
 {
 	if (nr_bvecs <= IO_CACHED_BVECS_SEGS)
-		return io_cache_alloc(&ctx->imu_cache, GFP_KERNEL);
-	return kvmalloc_flex(struct io_mapped_ubuf, bvec, nr_bvecs);
+		return io_cache_alloc(&ctx->imu_cache, GFP_KERNEL_ACCOUNT);
+	return kvmalloc_flex(struct io_mapped_ubuf, bvec, nr_bvecs,
+			     GFP_KERNEL_ACCOUNT);
 }
 
 static void io_free_imu(struct io_ring_ctx *ctx, struct io_mapped_ubuf *imu)
@@ -229,7 +230,7 @@ struct io_rsrc_node *io_rsrc_node_alloc(struct io_ring_ctx *ctx, int type)
 {
 	struct io_rsrc_node *node;
 
-	node = io_cache_alloc(&ctx->node_cache, GFP_KERNEL);
+	node = io_cache_alloc(&ctx->node_cache, GFP_KERNEL_ACCOUNT);
 	if (node) {
 		node->type = type;
 		node->refs = 1;
-- 
2.56.0


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

* [PATCH 7.3.y 09/16] io_uring: charge tctx nodes and personalities
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (7 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 08/16] io_uring/rsrc: charge buffer arrays and nodes to memcg Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 10/16] io_uring: free XArray nodes left by a failed store Jens Axboe
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 0b34e7b20a48710b6f53716bf7030920b69fdbdd upstream.

The io_tctx_node allocated the first time a task touches a ring, and
the XArray nodes behind tctx->xa and ctx->personalities, are plain
GFP_KERNEL. tctx->xa is indexed by the ring pointer, so nearly every
entry needs nodes of its own, and a task gets one entry per ring it has
used. 642 threads on 4000 rings held 2.3GB with 59MB charged against a
64MB memory.max.

Allocate the node with GFP_KERNEL_ACCOUNT and set XA_FLAGS_ACCOUNT on
both XArrays.

Fixes: 0f2122045b94 ("io_uring: don't rely on weak ->files references")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/io_uring.c | 2 +-
 io_uring/tctx.c     | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index dd6ad02d0e89..f218a1ec3f09 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -271,7 +271,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 	if (ret)
 		goto free_ref;
 	init_completion(&ctx->ref_comp);
-	xa_init_flags(&ctx->personalities, XA_FLAGS_ALLOC1);
+	xa_init_flags(&ctx->personalities, XA_FLAGS_ALLOC1 | XA_FLAGS_ACCOUNT);
 	mutex_init(&ctx->uring_lock);
 	init_waitqueue_head(&ctx->cq_wait);
 	init_waitqueue_head(&ctx->poll_wq);
diff --git a/io_uring/tctx.c b/io_uring/tctx.c
index 466b7300e208..4dedcdc44780 100644
--- a/io_uring/tctx.c
+++ b/io_uring/tctx.c
@@ -104,7 +104,7 @@ __cold struct io_uring_task *io_uring_alloc_task_context(struct task_struct *tas
 	}
 
 	tctx->task = task;
-	xa_init(&tctx->xa);
+	xa_init_flags(&tctx->xa, XA_FLAGS_ACCOUNT);
 	init_waitqueue_head(&tctx->wait);
 	atomic_set(&tctx->in_cancel, 0);
 	atomic_set(&tctx->inflight_tracked, 0);
@@ -123,7 +123,7 @@ static int io_tctx_install_node(struct io_ring_ctx *ctx,
 	if (xa_load(&tctx->xa, (unsigned long)ctx))
 		return 0;
 
-	node = kmalloc_obj(*node);
+	node = kmalloc_obj(*node, GFP_KERNEL_ACCOUNT);
 	if (!node)
 		return -ENOMEM;
 	node->ctx = ctx;
-- 
2.56.0


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

* [PATCH 7.3.y 10/16] io_uring: free XArray nodes left by a failed store
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (8 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 09/16] io_uring: charge tctx nodes and personalities Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 11/16] io_uring/bpf-ops: clear bpf_ctx while submitting SQEs Jens Axboe
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 51bab09b7ca1a9a6dd9303a3b7ee346f8128c963 upstream.

A failed xa_store() can leave nodes without entries in the tree, and
erasing every entry doesn't free those. An XArray needs xa_destroy()
before it goes away, even an empty one. That was dropped for tctx->xa in
commit ca6484cd308a ("io_uring: no need to call xa_destroy() on empty
xarray") over a lockdep report from freeing the tctx in softirq context,
and ctx->personalities never had it.

Now that the nodes are charged, a store can fail at memory.max and the
leaked nodes stay charged. Call xa_destroy() in io_uring_clean_tctx(),
which the task runs itself in process context before the tctx is freed,
in the error path of __io_uring_add_tctx_node(), and for
ctx->personalities in io_ring_ctx_free().

Fixes: ca6484cd308a ("io_uring: no need to call xa_destroy() on empty xarray")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/io_uring.c | 1 +
 io_uring/tctx.c     | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index f218a1ec3f09..880be1fed3a6 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -2297,6 +2297,7 @@ static __cold void io_ring_ctx_free(struct io_ring_ctx *ctx)
 	kvfree(ctx->cancel_table.hbs);
 	xa_destroy(&ctx->io_bl_xa);
 	xa_destroy(&ctx->hpage_acct);
+	xa_destroy(&ctx->personalities);
 	kfree(ctx);
 }
 
diff --git a/io_uring/tctx.c b/io_uring/tctx.c
index 4dedcdc44780..291e30e00e45 100644
--- a/io_uring/tctx.c
+++ b/io_uring/tctx.c
@@ -189,6 +189,7 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx)
 			io_wq_exit_start(tctx->io_wq);
 			io_wq_put_and_exit(tctx->io_wq);
 		}
+		xa_destroy(&tctx->xa);
 		percpu_counter_destroy(&tctx->inflight);
 		kfree(tctx);
 	}
@@ -250,6 +251,8 @@ __cold void io_uring_clean_tctx(struct io_uring_task *tctx)
 		io_uring_del_tctx_node(index);
 		cond_resched();
 	}
+	/* A failed xa_store() leaves nodes with no entry behind. */
+	xa_destroy(&tctx->xa);
 	if (wq) {
 		/*
 		 * Must be after io_uring_del_tctx_node() (removes nodes under
-- 
2.56.0


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

* [PATCH 7.3.y 11/16] io_uring/bpf-ops: clear bpf_ctx while submitting SQEs
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (9 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 10/16] io_uring: free XArray nodes left by a failed store Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 12/16] io_uring: free the task context after a grace period Jens Axboe
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 8543a39f03db8816a2d80d0c92c132a19f2afb48 upstream.

bpf_io_uring_submit_sqes() calls io_submit_sqes() from inside the loop
program, with current->bpf_ctx set, and requests issued inline run with
it set too. The socket code uses has_current_bpf_ctx() to recognize
calls from bpf_setsockopt() and friends, where the caller holds the
socket lock and has already been vetted, and skips both.

So a SOCKET_URING_OP_SETSOCKOPT from the SQ passes sockopt_capable()
without the capability, and runs without lock_sock(). A task with no
capability can set SO_MARK, IP_TRANSPARENT or TCP_REPAIR, and two
threads changing IP_OPTIONS on one socket, one through the ring, double
free in no time. SOCK_DESTROY via IORING_OP_SENDMSG ends up in
tcp_abort() without the lock the same way.

Clear the run context around io_submit_sqes(). The SQEs come from
userspace and must be issued like those of any other ring. They still
run inside the program's RCU tasks trace read section, so a request
that sleeps there holds up that grace period, but other request types
do that already.

Fixes: 890819248a86 ("io_uring/bpf-ops: add kfunc helpers")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/bpf-ops.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c
index cf2bd068e331..6c2a587fb1bf 100644
--- a/io_uring/bpf-ops.c
+++ b/io_uring/bpf-ops.c
@@ -17,8 +17,22 @@ __bpf_kfunc_start_defs();
 __bpf_kfunc int bpf_io_uring_submit_sqes(struct iou_ctx *loop_ctx, u32 nr)
 {
 	struct io_ring_ctx *ctx = io_loop_demangle_ctx(loop_ctx);
+	struct bpf_run_ctx *old_run_ctx;
+	int ret;
 
-	return io_submit_sqes(ctx, nr);
+	/*
+	 * The requests come from the SQ, which user space can write, so they
+	 * are issued for the task and not for the BPF program. Hide the run
+	 * context of the program from them. The users of has_current_bpf_ctx(),
+	 * such as sockopt_lock_sock() and sockopt_capable(), take a context for
+	 * a call from a BPF helper, whose caller holds the socket lock already
+	 * and needs no capability.
+	 */
+	old_run_ctx = bpf_set_run_ctx(NULL);
+	ret = io_submit_sqes(ctx, nr);
+	bpf_reset_run_ctx(old_run_ctx);
+
+	return ret;
 }
 
 __bpf_kfunc
-- 
2.56.0


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

* [PATCH 7.3.y 12/16] io_uring: free the task context after a grace period
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (10 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 11/16] io_uring/bpf-ops: clear bpf_ctx while submitting SQEs Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 13/16] io_uring/sqpoll: don't wait for the thread to exit Jens Axboe
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 9f5315d3f4368590c9fe06d4313b7247470bcf15 upstream.

A task other than the owner can free a request, io_ring_exit_work()
running the local task_work of a DEFER_TASKRUN ring for example, or
io_do_iopoll() reaping completions. io_put_task() then takes the else
branch:

    percpu_counter_sub(&tctx->inflight, 1);
    if (unlikely(atomic_read(&tctx->in_cancel)))
            wake_up(&tctx->wait);
    put_task_struct(tctx->task);

The first line is what lets the owner leave io_uring_cancel_generic(),
and on exec it frees the tctx right after. Nothing holds that off for
the remaining three lines, the task reference only covers a tctx freed
along with the task_struct. It used to work out because
io_uring_del_tctx_node() took the uring_lock that the freeing side holds
across io_put_task(), until that moved to tctx_lock.

Free the tctx with kfree_rcu(), and enter an RCU read section in
io_put_task() before the count is dropped.

Fixes: 5623eb1ed035 ("io_uring/tctx: add separate lock for list of tctx's in ctx")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 include/linux/io_uring_types.h | 3 +++
 io_uring/io_uring.c            | 2 ++
 io_uring/tctx.c                | 2 +-
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index c5edf6246a4b..1c85ce0e471d 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -161,6 +161,9 @@ struct io_uring_task {
 	/* drains ->task_list once the task can no longer run task_work */
 	struct work_struct		fallback_work;
 
+	/* the tctx is freed after a grace period, see io_put_task() */
+	struct rcu_head			rcu;
+
 	struct { /* task_work */
 		struct mpscq		task_list;
 		struct callback_head	task_work;
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 880be1fed3a6..c8aa6ca4a7cc 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -634,6 +634,8 @@ static inline void io_put_task(struct io_kiocb *req)
 	if (likely(tctx->task == current)) {
 		tctx->cached_refs++;
 	} else {
+		guard(rcu)();
+
 		percpu_counter_sub(&tctx->inflight, 1);
 		if (unlikely(atomic_read(&tctx->in_cancel)))
 			wake_up(&tctx->wait);
diff --git a/io_uring/tctx.c b/io_uring/tctx.c
index 291e30e00e45..b4fecea3ea47 100644
--- a/io_uring/tctx.c
+++ b/io_uring/tctx.c
@@ -64,7 +64,7 @@ void io_uring_free_tctx(struct task_struct *tsk)
 		WARN_ON_ONCE(tctx->cached_refs);
 
 		percpu_counter_destroy(&tctx->inflight);
-		kfree(tctx);
+		kfree_rcu(tctx, rcu);
 		tsk->io_uring = NULL;
 	}
 }
-- 
2.56.0


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

* [PATCH 7.3.y 13/16] io_uring/sqpoll: don't wait for the thread to exit
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (11 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 12/16] io_uring: free the task context after a grace period Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 14/16] io_uring: defer foreign io-wq enqueues to task_work Jens Axboe
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Chris Mason <mason@kernel.org>

Commit 360395e8dbf9d23579f74b84a1af11190813b0f8 upstream.

io_sq_thread_stop() wakes the SQPOLL thread and waits for it to exit.
The thread belongs to the thread group that created the ring, so a
SIGSTOP or the cgroup freezer holds it in get_signal(), and the wake up
doesn't change that. io_ring_exit_work() then sleeps in that wait until
someone continues or kills the process. iou_exit has 64 slots, so 64
closed SQPOLL rings of a stopped process keep every other ring from
being freed, for everyone.

Don't wait. Set the stop bit, wake the thread, and let it exit when it
gets to run again. The sqd can now outlive its last ring, so give it a
second refcount with one reference for the rings and one for the thread,
and free it with the last one. The thread can't reach a freed ring,
rings leave ctx_list before dropping their reference, and the io-wq hash
is refcounted.

Fixes: 16efa4fce3b7 ("io_uring: allow IO worker threads to be frozen")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/sqpoll.c | 28 +++++++++++++++++++++++-----
 io_uring/sqpoll.h |  3 ++-
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index ee97808a065a..49c52284ecdd 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -63,6 +63,24 @@ void io_sq_thread_park(struct io_sq_data *sqd)
 	}
 }
 
+/*
+ * The sqd has two holders: its rings, as one, and its thread. Neither waits
+ * for the other to let go. The one that lets go last frees the sqd.
+ */
+static void io_sq_data_release(struct io_sq_data *sqd)
+{
+	if (refcount_dec_and_test(&sqd->free_refs))
+		kfree(sqd);
+}
+
+/*
+ * Ask the thread to exit, and don't wait for it. A thread that is stopped or
+ * frozen exits only when a user lets it run again, and the exit work of the
+ * ring would keep its place in the workqueue until then. All rings have left
+ * ctx_list by now and have no request left, so the thread can reach none of
+ * them. What it still uses is its own or counted: the sqd by free_refs, the
+ * hash of its io-wq by hash->refs.
+ */
 void io_sq_thread_stop(struct io_sq_data *sqd)
 {
 	struct task_struct *tsk;
@@ -77,7 +95,6 @@ void io_sq_thread_stop(struct io_sq_data *sqd)
 		wake_up_process(tsk);
 	}
 	mutex_unlock(&sqd->lock);
-	wait_for_completion(&sqd->exited);
 }
 
 void io_put_sq_data(struct io_sq_data *sqd)
@@ -86,7 +103,7 @@ void io_put_sq_data(struct io_sq_data *sqd)
 		WARN_ON_ONCE(atomic_read(&sqd->park_pending));
 
 		io_sq_thread_stop(sqd);
-		kfree(sqd);
+		io_sq_data_release(sqd);
 	}
 }
 
@@ -160,10 +177,10 @@ static struct io_sq_data *io_get_sq_data(struct io_uring_params *p,
 
 	atomic_set(&sqd->park_pending, 0);
 	refcount_set(&sqd->refs, 1);
+	refcount_set(&sqd->free_refs, 2);
 	INIT_LIST_HEAD(&sqd->ctx_list);
 	mutex_init(&sqd->lock);
 	init_waitqueue_head(&sqd->wait);
-	init_completion(&sqd->exited);
 	return sqd;
 }
 
@@ -424,7 +441,7 @@ static int io_sq_thread(void *data)
 	io_run_task_work();
 	mutex_unlock(&sqd->lock);
 err_out:
-	complete(&sqd->exited);
+	io_sq_data_release(sqd);
 	do_exit(0);
 }
 
@@ -544,7 +561,8 @@ __cold int io_sq_offload_create(struct io_ring_ctx *ctx,
 	}
 	return 0;
 err_sqpoll:
-	complete(&ctx->sq_data->exited);
+	/* no thread was made: let go of its share */
+	io_sq_data_release(ctx->sq_data);
 err:
 	io_sq_thread_finish(ctx);
 	return ret;
diff --git a/io_uring/sqpoll.h b/io_uring/sqpoll.h
index fd2f6f29b516..024a51dc3069 100644
--- a/io_uring/sqpoll.h
+++ b/io_uring/sqpoll.h
@@ -2,6 +2,8 @@
 
 struct io_sq_data {
 	refcount_t		refs;
+	/* one for all the rings, one for the thread: the last put frees */
+	refcount_t		free_refs;
 	atomic_t		park_pending;
 	struct mutex		lock;
 
@@ -18,7 +20,6 @@ struct io_sq_data {
 
 	u64			work_time;
 	unsigned long		state;
-	struct completion	exited;
 };
 
 int io_sq_offload_create(struct io_ring_ctx *ctx, struct io_uring_params *p);
-- 
2.56.0


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

* [PATCH 7.3.y 14/16] io_uring: defer foreign io-wq enqueues to task_work
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (12 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 13/16] io_uring/sqpoll: don't wait for the thread to exit Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 15/16] io_uring/timeout: count cancelled timeouts in cq_timeouts Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 16/16] io_uring/net: fix multishot recv length limit accounting Jens Axboe
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Kyle Zeng <kylebot@openai.com>

Commit 3de16279b43dc285ed2222aba06cebdc82fdb28b upstream.

An IOPOLL ring without DEFER_TASKRUN can be reaped by a task other than
the one that submitted the I/O. If a polled request is marked
REQ_F_REISSUE, io_free_batch_list() calls io_queue_iowq() with the
original tctx. Ordinary task exit can tear down tctx->io_wq after
draining only the tracked requests. The live request pins the task, but
not its io-wq, so the foreign task can race io_wq_put_and_exit() and
enqueue through a freed or NULL queue.

Forward foreign userspace enqueues to the owning task before accessing
tctx->io_wq. Use a dedicated task_work callback so reissues still go
straight to io-wq, and fail them if task_work is running in cancellation
mode. Preserve the existing PF_KTHREAD cancellation path. This also
covers the other callers of io_queue_iowq() without changing the
worker-owned enqueue path.

Signed-off-by: Kyle Zeng <kylebot@openai.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/io_uring.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index c8aa6ca4a7cc..41a03979cb8b 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -423,6 +423,17 @@ static __always_inline bool io_assign_file(struct io_kiocb *req,
 	return !!req->file;
 }
 
+static void io_req_queue_iowq_tw(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+	struct io_kiocb *req = tw_req.req;
+
+	io_tw_lock(req->ctx, tw);
+	if (unlikely(tw.cancel))
+		io_req_defer_failed(req, -ECANCELED);
+	else
+		io_queue_iowq(req);
+}
+
 void io_queue_iowq(struct io_kiocb *req)
 {
 	struct io_uring_task *tctx = req->tctx;
@@ -430,10 +441,17 @@ void io_queue_iowq(struct io_kiocb *req)
 	BUG_ON(!tctx);
 	lockdep_assert_held(&req->ctx->uring_lock);
 
-	if ((current->flags & PF_KTHREAD) || !tctx->io_wq) {
-		io_req_task_queue_fail(req, -ECANCELED);
+	if (current->flags & PF_KTHREAD)
+		goto cancel;
+
+	/* Only the submitter can safely access its io-wq during task exit. */
+	if (unlikely(current != tctx->task)) {
+		req->io_task_work.func = io_req_queue_iowq_tw;
+		io_req_task_work_add(req);
 		return;
 	}
+	if (!tctx->io_wq)
+		goto cancel;
 
 	/*
 	 * The file is looked up as late as possible, as it may be the result
@@ -462,6 +480,9 @@ void io_queue_iowq(struct io_kiocb *req)
 
 	trace_io_uring_queue_async_work(req, io_wq_is_hashed(&req->work));
 	io_wq_enqueue(tctx->io_wq, &req->work);
+	return;
+cancel:
+	io_req_task_queue_fail(req, -ECANCELED);
 }
 
 unsigned io_linked_nr(struct io_kiocb *req)
-- 
2.56.0


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

* [PATCH 7.3.y 15/16] io_uring/timeout: count cancelled timeouts in cq_timeouts
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (13 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 14/16] io_uring: defer foreign io-wq enqueues to task_work Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  2026-11-04 18:44 ` [PATCH 7.3.y 16/16] io_uring/net: fix multishot recv length limit accounting Jens Axboe
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Cheng Yongkang <teel4res@gmail.com>

Commit 1c8b49851543f41fc8bb18bcd1f15cc93d14b530 upstream.

io_timeout_cancel() posts the cancelled timeout's -ECANCELED CQE without
advancing ctx->cq_timeouts.  io_timeout_fn() and io_kill_timeout()
advance it for every timeout CQE they post, and io_flush_timeouts()
computes the number of non-timeout completions as

    seq = cached_cq_tail - cq_timeouts

to compare against each off timeout's target_seq.  The cancelled
timeout's CQE is therefore counted as a regular completion, so every
cancellation makes an already armed off timeout fire one completion
early.

A ring with a keep-alive timeout of off=4 completes it after two NOPs
when one off=100 victim is cancelled through IORING_OP_ASYNC_CANCEL, and
before any NOP when two victims are cancelled.  The cancel CQE should
count, but the cancelled timeouts' -ECANCELED CQEs must not: the
expected counts are three and two NOPs respectively.

Advance cq_timeouts when io_timeout_extract() succeeds, under
timeout_lock and before the -ECANCELED completion is queued, matching
io_timeout_fn() and io_kill_timeout().  The -ENOENT and -EALREADY paths
post no new timeout CQE and keep their accounting.  Both the
ASYNC_CANCEL and the TIMEOUT_REMOVE path go through io_timeout_cancel().

Fixes: bfe68a221905 ("io_uring: off timeouts based only on completions")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Cheng Yongkang <teel4res@gmail.com>
Link: https://patch.msgid.link/179328314078.648427.13761781546953594668@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/timeout.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index ca03cb95ad8d..8e7a4bd7e514 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -370,6 +370,9 @@ int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd)
 
 	raw_spin_lock_irq(&ctx->timeout_lock);
 	req = io_timeout_extract(ctx, cd);
+	if (!IS_ERR(req))
+		atomic_set(&ctx->cq_timeouts,
+			   atomic_read(&ctx->cq_timeouts) + 1);
 	raw_spin_unlock_irq(&ctx->timeout_lock);
 
 	if (IS_ERR(req))
-- 
2.56.0


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

* [PATCH 7.3.y 16/16] io_uring/net: fix multishot recv length limit accounting
  2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
                   ` (14 preceding siblings ...)
  2026-11-04 18:44 ` [PATCH 7.3.y 15/16] io_uring/timeout: count cancelled timeouts in cq_timeouts Jens Axboe
@ 2026-11-04 18:44 ` Jens Axboe
  15 siblings, 0 replies; 17+ messages in thread
From: Jens Axboe @ 2026-11-04 18:44 UTC (permalink / raw)
  To: stable

From: Cheng Yongkang <teel4res@gmail.com>

Commit 3e3aebb7570ccdde5cc49c9bfc8fd3a436b71b0c upstream.

io_recv_finish() debits sr->mshot_total_len with min_t(int, sel->val,
sr->mshot_total_len).  sel->val is the cumulative byte count of the
request: on a bundle retry the receive path adds sr->done_io back to the
return value, and the bundle branch in io_recv_finish() recovers the new
bytes with this_ret = sel->val - sr->done_io.  The debit therefore uses
the wrong value after a bundle retry, and the int cast truncates a limit
above INT_MAX.

For an IORING_OP_RECV multishot request with optlen above INT_MAX, the
limit becomes negative when cast to int, so the first completion debits
the entire limit and ends the multishot with IORING_RECV_MSHOT_DONE
while the socket still holds data that covers the limit.  With 120 bytes
queued, a 64-byte buffer and optlen 0x80000000, the first 64-byte CQE
lacks IORING_CQE_F_MORE and 56 bytes remain unread; the same setup with
optlen 0x7fffffff posts 64 and 56 bytes with F_MORE.

After a bundle retry, the bytes already debited for the previous
attempts are debited again, so the limit is exhausted early.  With 1400
bytes queued, optlen 1000 and buffers of 600, 100, 100, 50, 50 and 200
bytes, the first attempt delivers 700 bytes and retries; the second
delivers 200 more, but the debit accounts for 900 and finishes the
request with a terminal 900-byte CQE while 500 bytes remain unread.  The
limit could have been reached.

Debit only the bytes received by this invocation, sel->val -
sr->done_io, and compare as unsigned so a limit above INT_MAX is
accounted correctly. Non-bundle multishot requests keep sr->done_io at
zero, so their accounting is unchanged.

Fixes: 0ebc9a7ecf6a ("io_uring/net: Support multishot receive len cap")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Cheng Yongkang <teel4res@gmail.com>
Link: https://patch.msgid.link/179328314078.648427.10086632105828535293@gmail.com
[axboe: fix-up near-by incorrect comment while in there]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 io_uring/net.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/io_uring/net.c b/io_uring/net.c
index b62f21df42f2..4b36883e101b 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -881,12 +881,15 @@ static inline bool io_recv_finish(struct io_kiocb *req,
 		cflags |= IORING_CQE_F_SOCK_NONEMPTY;
 
 	if (sel->val > 0 && sr->flags & IORING_RECV_MSHOT_LIM) {
+		unsigned int this_len = sel->val - sr->done_io;
+
 		/*
-		 * If sr->len hits zero, the limit has been reached. Mark
-		 * mshot as finished, and flag MSHOT_DONE as well to prevent
-		 * a potential bundle from being retried.
+		 * If sr->mshot_total_len hits zero, the limit has been reached.
+		 * Mark mshot as finished, and flag MSHOT_DONE as well to
+		 * prevent a potential bundle from being retried.
 		 */
-		sr->mshot_total_len -= min_t(int, sel->val, sr->mshot_total_len);
+		sr->mshot_total_len -= min_t(unsigned int, this_len,
+					     sr->mshot_total_len);
 		if (!sr->mshot_total_len) {
 			sr->flags |= IORING_RECV_MSHOT_DONE;
 			mshot_finished = true;
-- 
2.56.0


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

end of thread, other threads:[~2026-11-04 18:45 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 18:44 [PATCH 7.3.y 00/16] io_uring fixes for 7.3-stable Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 01/16] io_uring/kbuf: advance head when a pickup empties the buffer ring Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 02/16] io_uring/net: keep recvmsg buffer select length within MAX_RW_COUNT Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 03/16] io_uring/net: mask socket type with SOCK_TYPE_MASK in io_socket_bpf_populate() Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 04/16] io_uring/register: enforce per-task restrictions in io_uring_register_blind() Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 05/16] io_uring/net: don't retry a non-blocking connect() that fails with -EAGAIN Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 06/16] io_uring: keep the drain active when a deferred drain request is started Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 07/16] io_uring/io-wq: no new workers once the exit has begun Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 08/16] io_uring/rsrc: charge buffer arrays and nodes to memcg Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 09/16] io_uring: charge tctx nodes and personalities Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 10/16] io_uring: free XArray nodes left by a failed store Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 11/16] io_uring/bpf-ops: clear bpf_ctx while submitting SQEs Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 12/16] io_uring: free the task context after a grace period Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 13/16] io_uring/sqpoll: don't wait for the thread to exit Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 14/16] io_uring: defer foreign io-wq enqueues to task_work Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 15/16] io_uring/timeout: count cancelled timeouts in cq_timeouts Jens Axboe
2026-11-04 18:44 ` [PATCH 7.3.y 16/16] io_uring/net: fix multishot recv length limit accounting Jens Axboe

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