From: Jens Axboe <axboe@kernel.dk>
To: Dan Carpenter <error27@gmail.com>
Cc: io-uring@vger.kernel.org
Subject: Re: [bug report] io_uring/register: fix ring resizing with mixed/large SQEs/CQEs
Date: Fri, 24 Apr 2026 09:01:25 -0600 [thread overview]
Message-ID: <f04047a8-4101-4995-94b1-6afdf0f80d94@kernel.dk> (raw)
In-Reply-To: <aesA-_AEKgF_oucO@stanley.mountain>
On 4/23/26 11:34 PM, Dan Carpenter wrote:
> Hello Jens Axboe,
>
> Commit 45cd95763e19 ("io_uring/register: fix ring resizing with
> mixed/large SQEs/CQEs") from Apr 20, 2026 (linux-next), leads to the
> following Smatch static checker warning:
>
> io_uring/register.c:613 io_register_resize_rings()
> warn: potential integer overflow from user (local copy) 'p->sq_entries << 1'
>
> io_uring/register.c:643 io_register_resize_rings()
> warn: potential integer overflow from user (local copy) 'p->cq_entries << 1'
>
> io_uring/register.c
> 498 static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg)
> 499 {
> 500 struct io_ctx_config config;
> 501 struct io_uring_region_desc rd;
> 502 struct io_ring_ctx_rings o = { }, n = { }, *to_free = NULL;
> 503 unsigned i, tail, old_head;
> 504 struct io_uring_params *p = &config.p;
> 505 struct io_rings_layout *rl = &config.layout;
> 506 int ret;
> 507
> 508 memset(&config, 0, sizeof(config));
> 509
> 510 /* limited to DEFER_TASKRUN for now */
> 511 if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
> 512 return -EINVAL;
> 513 if (copy_from_user(p, arg, sizeof(*p)))
>
> p comes from the user. p->sq_entries and p->cq_entries are u32.
>
> 514 return -EFAULT;
> 515 if (p->flags & ~RESIZE_FLAGS)
> 516 return -EINVAL;
> 517
> 518 /* properties that are always inherited */
> 519 p->flags |= (ctx->flags & COPY_FLAGS);
> 520
> 521 ret = io_prepare_config(&config);
> 522 if (unlikely(ret))
> 523 return ret;
> 524
> 525 memset(&rd, 0, sizeof(rd));
> 526 rd.size = PAGE_ALIGN(rl->rings_size);
> 527 if (p->flags & IORING_SETUP_NO_MMAP) {
> 528 rd.user_addr = p->cq_off.user_addr;
> 529 rd.flags |= IORING_MEM_REGION_TYPE_USER;
> 530 }
> 531 ret = io_create_region(ctx, &n.ring_region, &rd, IORING_OFF_CQ_RING);
> 532 if (ret)
> 533 return ret;
> 534
> 535 n.rings = io_region_get_ptr(&n.ring_region);
> 536
> 537 /*
> 538 * At this point n.rings is shared with userspace, just like o.rings
> 539 * is as well. While we don't expect userspace to modify it while
> 540 * a resize is in progress, and it's most likely that userspace will
> 541 * shoot itself in the foot if it does, we can't always assume good
> 542 * intent... Use read/write once helpers from here on to indicate the
> 543 * shared nature of it.
> 544 */
> 545 WRITE_ONCE(n.rings->sq_ring_mask, p->sq_entries - 1);
> 546 WRITE_ONCE(n.rings->cq_ring_mask, p->cq_entries - 1);
>
> If p->sq_entries is 0 then this wraps to U32_MAX
>
> 547 WRITE_ONCE(n.rings->sq_ring_entries, p->sq_entries);
> 548 WRITE_ONCE(n.rings->cq_ring_entries, p->cq_entries);
> 549
> 550 if (copy_to_user(arg, p, sizeof(*p))) {
> 551 io_register_free_rings(ctx, &n);
> 552 return -EFAULT;
> 553 }
> 554
> 555 memset(&rd, 0, sizeof(rd));
> 556 rd.size = PAGE_ALIGN(rl->sq_size);
> 557 if (p->flags & IORING_SETUP_NO_MMAP) {
> 558 rd.user_addr = p->sq_off.user_addr;
> 559 rd.flags |= IORING_MEM_REGION_TYPE_USER;
> 560 }
> 561 ret = io_create_region(ctx, &n.sq_region, &rd, IORING_OFF_SQES);
> 562 if (ret) {
> 563 io_register_free_rings(ctx, &n);
> 564 return ret;
> 565 }
> 566 n.sq_sqes = io_region_get_ptr(&n.sq_region);
> 567
> 568 /*
> 569 * If using SQPOLL, park the thread
> 570 */
> 571 if (ctx->sq_data) {
> 572 mutex_unlock(&ctx->uring_lock);
> 573 io_sq_thread_park(ctx->sq_data);
> 574 mutex_lock(&ctx->uring_lock);
> 575 }
> 576
> 577 /*
> 578 * We'll do the swap. Grab the ctx->mmap_lock, which will exclude
> 579 * any new mmap's on the ring fd. Clear out existing mappings to prevent
> 580 * mmap from seeing them, as we'll unmap them. Any attempt to mmap
> 581 * existing rings beyond this point will fail. Not that it could proceed
> 582 * at this point anyway, as the io_uring mmap side needs go grab the
> 583 * ctx->mmap_lock as well. Likewise, hold the completion lock over the
> 584 * duration of the actual swap.
> 585 */
> 586 mutex_lock(&ctx->mmap_lock);
> 587 spin_lock(&ctx->completion_lock);
> 588 o.rings = ctx->rings;
> 589 ctx->rings = NULL;
> 590 o.sq_sqes = ctx->sq_sqes;
> 591 ctx->sq_sqes = NULL;
> 592
> 593 /*
> 594 * Now copy SQ and CQ entries, if any. If either of the destination
> 595 * rings can't hold what is already there, then fail the operation.
> 596 */
> 597 tail = READ_ONCE(o.rings->sq.tail);
> 598 old_head = READ_ONCE(o.rings->sq.head);
> 599 if (tail - old_head > p->sq_entries)
> 600 goto overflow;
>
> I guess if p->sq_entries were zero then we would hit this goto
>
> 601 for (i = old_head; i < tail; i++) {
> 602 unsigned index, dst_mask, src_mask;
> 603 size_t sq_size;
> 604
> 605 index = i;
> 606 sq_size = sizeof(struct io_uring_sqe);
> 607 src_mask = ctx->sq_entries - 1;
> 608 dst_mask = p->sq_entries - 1;
> 609 if (ctx->flags & IORING_SETUP_SQE128) {
> 610 index <<= 1;
> 611 sq_size <<= 1;
> 612 src_mask = (ctx->sq_entries << 1) - 1;
> --> 613 dst_mask = (p->sq_entries << 1) - 1;
>
> These shifts could integer overflow. So if you picked p->sq_entries
> which was (1U << 31) then the (p->sq_entries << 1) would be zero and
> the mask would be 0xffffffff. Which might even be intentional, since
> overflowing to zero and subtracting one is an idiom...
These are both fine, as SQ entries are capped at IORING_MAX_ENTRIES
(32k) and CQ entries are capped at IORING_MAX_CQ_ENTRIES (64k). Anything
larger is rejected earlier, as is 0.
IOW, false positives.
--
Jens Axboe
prev parent reply other threads:[~2026-04-24 15:01 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-24 5:34 [bug report] io_uring/register: fix ring resizing with mixed/large SQEs/CQEs Dan Carpenter
2026-04-24 15:01 ` Jens Axboe [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f04047a8-4101-4995-94b1-6afdf0f80d94@kernel.dk \
--to=axboe@kernel.dk \
--cc=error27@gmail.com \
--cc=io-uring@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox