Skip to content

Commit fa58e6e

Browse files
committed
Merge tag 'io_uring-7.1-20260424' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull io_uring fixes from Jens Axboe: - Fix for a NOMMU bug with io_uring, where NOMMU doesn't grab page refs at mmap time. NOMMU also has entirely broken FOLL_PIN support, yet here we are - A few fixes covering minor issues introduced in this merge window - data race annotation to shut up KCSAN for when io-wq limits are applied - A nospec addition for direct descriptor file updating. Rest of the direct descriptor path already had this, but for some reason the update did not. Now they are all the same - Various minor defensive changes that claude identified and suggested terrible fixes for, turned into actually useful cleanups: - Use kvfree() for the imu cache. These can come from kmalloc or vmalloc depending on size, but the in-cache ones are capped where it's always kmalloc based. Change to kvfree() in the cleanup path, making future changes unlikely to mess that up - Negative kbuf consumption lengths. Can't happen right now, but cqe->res is used directly, which if other codes changes could then be an error value - Fix for an issue with the futex code, where partial wakes on a vectored fuxes would potentially wake the same futex twice, rather than move on to the next one. This could confuse an application as it would've expected the next futex to have been woken - Fix for a bug with ring resizing, where SQEs or CQEs might not have been copied correctly if large SQEs or CQEs are used in the ring. Application side issue, where SQEs or CQEs might have been lost during resize - Fix for a bug where EPOLL_URING_WAKE might have been lost, causing a multishot poll to not be terminated when it's nested, like it should have been - Fix for an issue with signed comparison of poll references for the slow path - Fix for a user struct UAF in the zcrx code - Two minor zcrx cleanups * tag 'io_uring-7.1-20260424' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: io_uring: take page references for NOMMU pbuf_ring mmaps io_uring/poll: ensure EPOLL_ONESHOT is propagated for EPOLL_URING_WAKE io_uring/zcrx: warn on freelist violations io_uring/zcrx: clear RQ headers on init io_uring/zcrx: fix user_struct uaf io_uring/register: fix ring resizing with mixed/large SQEs/CQEs io_uring/futex: ensure partial wakes are appropriately dequeued io_uring/rw: add defensive hardening for negative kbuf lengths io_uring/rsrc: use kvfree() for the imu cache io_uring/rsrc: unify nospec indexing for direct descriptors io_uring: fix spurious fput in registered ring path io_uring: fix iowq_limits data race in tctx node addition io_uring/tctx: mark io_wq as exiting before error path teardown io_uring/tctx: check for setup tctx->io_wq before teardown io_uring/poll: fix signed comparison in io_poll_get_ownership()
2 parents b85900e + d0be888 commit fa58e6e

11 files changed

Lines changed: 111 additions & 24 deletions

File tree

io_uring/alloc_cache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static inline void *io_cache_alloc(struct io_alloc_cache *cache, gfp_t gfp)
6464
static inline void io_cache_free(struct io_alloc_cache *cache, void *obj)
6565
{
6666
if (!io_alloc_cache_put(cache, obj))
67-
kfree(obj);
67+
kvfree(obj);
6868
}
6969

7070
#endif

io_uring/futex.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,10 @@ static void io_futex_wakev_fn(struct wake_q_head *wake_q, struct futex_q *q)
159159
struct io_kiocb *req = q->wake_data;
160160
struct io_futexv_data *ifd = req->async_data;
161161

162-
if (!io_futexv_claim(ifd))
162+
if (!io_futexv_claim(ifd)) {
163+
__futex_wake_mark(q);
163164
return;
165+
}
164166
if (unlikely(!__futex_wake_mark(q)))
165167
return;
166168

io_uring/io_uring.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2575,7 +2575,8 @@ struct file *io_uring_ctx_get_file(unsigned int fd, bool registered)
25752575
return ERR_PTR(-EBADF);
25762576
if (io_is_uring_fops(file))
25772577
return file;
2578-
fput(file);
2578+
if (!registered)
2579+
fput(file);
25792580
return ERR_PTR(-EOPNOTSUPP);
25802581
}
25812582

io_uring/memmap.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,53 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr,
366366

367367
#else /* !CONFIG_MMU */
368368

369+
/*
370+
* Drop the pages that were initially referenced and added in
371+
* io_uring_mmap(). We cannot have had a mremap() as that isn't supported,
372+
* hence the vma should be identical to the one we initially referenced and
373+
* mapped, and partial unmaps and splitting isn't possible on a file backed
374+
* mapping.
375+
*/
376+
static void io_uring_nommu_vm_close(struct vm_area_struct *vma)
377+
{
378+
unsigned long index;
379+
380+
for (index = vma->vm_start; index < vma->vm_end; index += PAGE_SIZE)
381+
put_page(virt_to_page((void *) index));
382+
}
383+
384+
static const struct vm_operations_struct io_uring_nommu_vm_ops = {
385+
.close = io_uring_nommu_vm_close,
386+
};
387+
369388
int io_uring_mmap(struct file *file, struct vm_area_struct *vma)
370389
{
371-
return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -EINVAL;
390+
struct io_ring_ctx *ctx = file->private_data;
391+
struct io_mapped_region *region;
392+
unsigned long i;
393+
394+
if (!is_nommu_shared_mapping(vma->vm_flags))
395+
return -EINVAL;
396+
397+
guard(mutex)(&ctx->mmap_lock);
398+
region = io_mmap_get_region(ctx, vma->vm_pgoff);
399+
if (!region || !io_region_is_set(region))
400+
return -EINVAL;
401+
402+
if ((vma->vm_end - vma->vm_start) !=
403+
(unsigned long) region->nr_pages << PAGE_SHIFT)
404+
return -EINVAL;
405+
406+
/*
407+
* Pin the pages so io_free_region()'s release_pages() does not
408+
* drop the last reference while this VMA exists. delete_vma()
409+
* in mm/nommu.c calls vma_close() which runs ->close above.
410+
*/
411+
for (i = 0; i < region->nr_pages; i++)
412+
get_page(region->pages[i]);
413+
414+
vma->vm_ops = &io_uring_nommu_vm_ops;
415+
return 0;
372416
}
373417

374418
unsigned int io_uring_nommu_mmap_capabilities(struct file *file)

io_uring/poll.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static bool io_poll_get_ownership_slowpath(struct io_kiocb *req)
9393
*/
9494
static inline bool io_poll_get_ownership(struct io_kiocb *req)
9595
{
96-
if (unlikely(atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS))
96+
if (unlikely((unsigned int)atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS))
9797
return io_poll_get_ownership_slowpath(req);
9898
return !(atomic_fetch_inc(&req->poll_refs) & IO_POLL_REF_MASK);
9999
}
@@ -417,8 +417,10 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
417417
* disable multishot as there is a circular dependency between
418418
* CQ posting and triggering the event.
419419
*/
420-
if (mask & EPOLL_URING_WAKE)
420+
if (mask & EPOLL_URING_WAKE) {
421421
poll->events |= EPOLLONESHOT;
422+
req->apoll_events |= EPOLLONESHOT;
423+
}
422424

423425
/* optional, saves extra locking for removal in tw handler */
424426
if (mask && poll->events & EPOLLONESHOT) {

io_uring/register.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -599,10 +599,20 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg)
599599
if (tail - old_head > p->sq_entries)
600600
goto overflow;
601601
for (i = old_head; i < tail; i++) {
602-
unsigned src_head = i & (ctx->sq_entries - 1);
603-
unsigned dst_head = i & (p->sq_entries - 1);
604-
605-
n.sq_sqes[dst_head] = o.sq_sqes[src_head];
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;
614+
}
615+
memcpy(&n.sq_sqes[index & dst_mask], &o.sq_sqes[index & src_mask], sq_size);
606616
}
607617
WRITE_ONCE(n.rings->sq.head, old_head);
608618
WRITE_ONCE(n.rings->sq.tail, tail);
@@ -619,10 +629,20 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg)
619629
goto out;
620630
}
621631
for (i = old_head; i < tail; i++) {
622-
unsigned src_head = i & (ctx->cq_entries - 1);
623-
unsigned dst_head = i & (p->cq_entries - 1);
624-
625-
n.rings->cqes[dst_head] = o.rings->cqes[src_head];
632+
unsigned index, dst_mask, src_mask;
633+
size_t cq_size;
634+
635+
index = i;
636+
cq_size = sizeof(struct io_uring_cqe);
637+
src_mask = ctx->cq_entries - 1;
638+
dst_mask = p->cq_entries - 1;
639+
if (ctx->flags & IORING_SETUP_CQE32) {
640+
index <<= 1;
641+
cq_size <<= 1;
642+
src_mask = (ctx->cq_entries << 1) - 1;
643+
dst_mask = (p->cq_entries << 1) - 1;
644+
}
645+
memcpy(&n.rings->cqes[index & dst_mask], &o.rings->cqes[index & src_mask], cq_size);
626646
}
627647
WRITE_ONCE(n.rings->cq.head, old_head);
628648
WRITE_ONCE(n.rings->cq.tail, tail);

io_uring/rsrc.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ bool io_rsrc_cache_init(struct io_ring_ctx *ctx)
168168
void io_rsrc_cache_free(struct io_ring_ctx *ctx)
169169
{
170170
io_alloc_cache_free(&ctx->node_cache, kfree);
171-
io_alloc_cache_free(&ctx->imu_cache, kfree);
171+
io_alloc_cache_free(&ctx->imu_cache, kvfree);
172172
}
173173

174174
static void io_clear_table_tags(struct io_rsrc_data *data)
@@ -238,6 +238,9 @@ static int __io_sqe_files_update(struct io_ring_ctx *ctx,
238238
continue;
239239

240240
i = up->offset + done;
241+
if (i >= ctx->file_table.data.nr)
242+
break;
243+
i = array_index_nospec(i, ctx->file_table.data.nr);
241244
if (io_reset_rsrc_node(ctx, &ctx->file_table.data, i))
242245
io_file_bitmap_clear(&ctx->file_table, i);
243246

io_uring/rsrc.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,15 @@ static inline void io_put_rsrc_node(struct io_ring_ctx *ctx, struct io_rsrc_node
109109
}
110110

111111
static inline bool io_reset_rsrc_node(struct io_ring_ctx *ctx,
112-
struct io_rsrc_data *data, int index)
112+
struct io_rsrc_data *data,
113+
unsigned int index)
113114
{
114-
struct io_rsrc_node *node = data->nodes[index];
115+
struct io_rsrc_node *node;
115116

117+
if (index >= data->nr)
118+
return false;
119+
index = array_index_nospec(index, data->nr);
120+
node = data->nodes[index];
116121
if (!node)
117122
return false;
118123
io_put_rsrc_node(ctx, node);

io_uring/rw.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw)
580580
io_req_io_end(req);
581581

582582
if (req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING))
583-
req->cqe.flags |= io_put_kbuf(req, req->cqe.res, NULL);
583+
req->cqe.flags |= io_put_kbuf(req, max(req->cqe.res, 0), NULL);
584584

585585
io_req_rw_cleanup(req, 0);
586586
io_req_task_complete(tw_req, tw);
@@ -1379,7 +1379,7 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin)
13791379
list_del(&req->iopoll_node);
13801380
wq_list_add_tail(&req->comp_list, &ctx->submit_state.compl_reqs);
13811381
nr_events++;
1382-
req->cqe.flags = io_put_kbuf(req, req->cqe.res, NULL);
1382+
req->cqe.flags = io_put_kbuf(req, max(req->cqe.res, 0), NULL);
13831383
if (!io_is_uring_cmd(req))
13841384
io_req_rw_cleanup(req, 0);
13851385
}

io_uring/tctx.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,13 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx)
146146
if (IS_ERR(tctx))
147147
return PTR_ERR(tctx);
148148

149-
if (ctx->int_flags & IO_RING_F_IOWQ_LIMITS_SET) {
150-
unsigned int limits[2] = { ctx->iowq_limits[0],
151-
ctx->iowq_limits[1], };
149+
if (data_race(ctx->int_flags) & IO_RING_F_IOWQ_LIMITS_SET) {
150+
unsigned int limits[2];
151+
152+
mutex_lock(&ctx->uring_lock);
153+
limits[0] = ctx->iowq_limits[0];
154+
limits[1] = ctx->iowq_limits[1];
155+
mutex_unlock(&ctx->uring_lock);
152156

153157
ret = io_wq_max_workers(tctx->io_wq, limits);
154158
if (ret)
@@ -171,7 +175,10 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx)
171175
}
172176
if (!current->io_uring) {
173177
err_free:
174-
io_wq_put_and_exit(tctx->io_wq);
178+
if (tctx->io_wq) {
179+
io_wq_exit_start(tctx->io_wq);
180+
io_wq_put_and_exit(tctx->io_wq);
181+
}
175182
percpu_counter_destroy(&tctx->inflight);
176183
kfree(tctx);
177184
}

0 commit comments

Comments
 (0)