[PATCH] io_uring: fix oversize cq.head distance in io_cqring_queued()

Yang Xiuwei <[email protected]> Tue, 21 Jul 2026 18:13:06 +0800
Newsgroups org.kernel.vger.io-uring
Message-ID <[email protected]>
The previous cq.head validation treats head past cached_cq_tail as an
empty ring. When head is rewound so the unsigned distance exceeds
cq_entries, we still used min() and reported a full ring.

Posting then fails into overflow without advancing cq.tail, and
io_cqring_wait() keeps retrying on the overflow bit. A valid distance
is only [0, cq_entries]; reject anything outside that range the same
way as head-past-tail.

Fixes: f44d38a31f18 ("io_uring: validate user-controlled cq.head in io_cqe_cache_refill()")
Assisted-by: Cursor:Composer
Signed-off-by: Yang Xiuwei <[email protected]>
---
 io_uring/io_uring.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 1ea2fca34a36..da9f5971e0c9 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -688,16 +688,17 @@ static struct io_overflow_cqe *io_alloc_ocqe(struct io_ring_ctx *ctx,
 }
 
 /*
- * Compute queued CQEs for free-space calculation, clamped to cq_entries.
+ * cq.head is controlled by userspace. Only a distance of [0, cq_entries] is
+ * valid; anything outside that range is a bogus head, treat as empty.
  */
 static unsigned int io_cqring_queued(struct io_ring_ctx *ctx)
 {
 	struct io_rings *rings = io_get_rings(ctx);
-	int diff;
+	unsigned int head = READ_ONCE(rings->cq.head);
+	unsigned int diff = ctx->cached_cq_tail - head;
 
-	diff = (int)(ctx->cached_cq_tail - READ_ONCE(rings->cq.head));
-	if (diff >= 0)
-		return min((unsigned int)diff, ctx->cq_entries);
+	if (diff <= ctx->cq_entries)
+		return diff;
 	return 0;
 }
 
-- 
2.25.1