diff options
| author | Josef Bacik <josef@toxicpanda.com> | 2026-10-15 19:30:51 +0000 |
|---|---|---|
| committer | Jens Axboe <axboe@kernel.dk> | 2026-10-15 14:17:57 -0600 |
| commit | 0feb5bcd2d4261d656729892aef4d643461ea5ac (patch) | |
| tree | 598742383e80ade32f553416b8d3cdb5016ce68f | |
| parent | e8dee8d5498c710a22a84076d8ecb661627032b4 (diff) | |
| download | linux-0feb5bcd2d4261d656729892aef4d643461ea5ac.tar.gz linux-0feb5bcd2d4261d656729892aef4d643461ea5ac.zip | |
io_uring/register: keep 128b SQEs intact on resize
On a ring with IORING_SETUP_SQE_MIXED a 128b SQE takes two entries that
have to be contiguous in memory, io_init_req() fails it with -EINVAL if
it starts on the last entry of the SQ ring. The entry a pending SQE ends
up in after IORING_REGISTER_RESIZE_RINGS depends on the size of the new
ring, and nothing takes care of that. With four entries instead of eight,
a 128b SQE which was fine in entries 3 and 4 is split up between entry 3
and entry 0. The resize succeeds, and the request fails later on.
If the ring has an SQ array, the second half is also taken from the
wrong place unless the array happens to map the following index to the
following entry. The submission side doesn't look at the array for the
second half.
Do what is done for 32b CQEs on IORING_SETUP_CQE_MIXED rings: fail the
resize with -EOVERFLOW if a 128b SQE would wrap. And copy the second half
from where it is.
Fixes: 1cba30bf9fdd ("io_uring: add support for IORING_SETUP_SQE_MIXED")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-27-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
| -rw-r--r-- | io_uring/register.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/io_uring/register.c b/io_uring/register.c index bce4473dc2dd..2ce36623e25a 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -496,6 +496,17 @@ static void io_register_free_rings(struct io_ring_ctx *ctx, IORING_SETUP_CQE32 | IORING_SETUP_NO_MMAP | \ IORING_SETUP_CQE_MIXED | IORING_SETUP_SQE_MIXED) +static bool io_sqe_is_128(struct io_ring_ctx *ctx, + const struct io_uring_sqe *sqe) +{ + u8 opcode = READ_ONCE(sqe->opcode); + + if (!(ctx->flags & IORING_SETUP_SQE_MIXED) || opcode >= IORING_OP_LAST) + return false; + opcode = array_index_nospec(opcode, IORING_OP_LAST); + return io_issue_defs[opcode].is_128; +} + static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) { struct io_ctx_config config; @@ -506,6 +517,7 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) struct io_rings_layout *rl = &config.layout; u32 *o_sq_array, *n_sq_array = NULL; bool cqe32_tail = false; + unsigned int sqe128_src = 0; int ret; memset(&config, 0, sizeof(config)); @@ -605,12 +617,19 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) if (tail - old_head > p->sq_entries) goto overflow; for (i = old_head; i != tail; i++) { + bool sqe128_tail = sqe128_src; unsigned int dst, src; size_t sq_size; dst = i & (p->sq_entries - 1); src = i & (ctx->sq_entries - 1); - if (n_sq_array) { + if (sqe128_tail) { + /* it follows the first half, whatever the array says */ + src = sqe128_src; + sqe128_src = 0; + if (n_sq_array) + WRITE_ONCE(n_sq_array[dst], dst); + } else if (n_sq_array) { src = READ_ONCE(o_sq_array[src]); if (unlikely(src >= ctx->sq_entries)) { WRITE_ONCE(n_sq_array[dst], UINT_MAX); @@ -619,6 +638,19 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) WRITE_ONCE(n_sq_array[dst], dst); } + /* + * A 128b SQE on a SQE_MIXED ring takes two contiguous entries + * and must not wrap. A smaller ring may place it on the last + * entry, fail the resize if that happens. + */ + if (!sqe128_tail && i + 1 != tail && + src < ctx->sq_entries - 1 && + io_sqe_is_128(ctx, &o.sq_sqes[src])) { + if (dst == p->sq_entries - 1) + goto overflow; + sqe128_src = src + 1; + } + sq_size = sizeof(struct io_uring_sqe); if (ctx->flags & IORING_SETUP_SQE128) { dst <<= 1; |
