[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