From: Koba Ko When io_pin_pages() succeeds but the subsequent nr_pages sanity check fires (WARN_ON_ONCE), the function returns -EFAULT without unpinning the user pages or freeing the kvmalloc'd pages array. The caller's cleanup via io_free_region() won't help either, because mr->pages was never assigned — so the entire cleanup block is skipped. Add unpin_user_pages() and kvfree() before the error return to prevent the leak. Fixes: a90558b36ccee ("io_uring/memmap: helper for pinning region pages") Signed-off-by: Koba Ko --- io_uring/memmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io_uring/memmap.c b/io_uring/memmap.c index e6958968975a8..9f0d3750ce3bc 100644 --- a/io_uring/memmap.c +++ b/io_uring/memmap.c @@ -141,8 +141,11 @@ static int io_region_pin_pages(struct io_mapped_region *mr, pages = io_pin_pages(reg->user_addr, size, &nr_pages); if (IS_ERR(pages)) return PTR_ERR(pages); - if (WARN_ON_ONCE(nr_pages != mr->nr_pages)) + if (WARN_ON_ONCE(nr_pages != mr->nr_pages)) { + unpin_user_pages(pages, nr_pages); + kvfree(pages); return -EFAULT; + } mr->pages = pages; mr->flags |= IO_REGION_F_USER_PROVIDED; -- 2.43.0 From: Koba Ko io_buffer_register_bvec() allocates the rsrc node via io_rsrc_node_alloc() which pulls from ctx->node_cache. On imu allocation failure, the node is freed with raw kfree() instead of io_cache_free(&ctx->node_cache, node), bypassing the cache return path and wasting a reuse opportunity. Every other error path in this file correctly uses io_cache_free for nodes. Fixes: 27cb27b6d5ea4 ("io_uring: add support for kernel registered bvecs") Signed-off-by: Koba Ko --- io_uring/rsrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 1b96ab5e98c99..6f46cf9cd13d7 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -961,7 +961,7 @@ int io_buffer_register_bvec(struct io_uring_cmd *cmd, struct request *rq, */ imu = io_alloc_imu(ctx, blk_rq_nr_phys_segments(rq)); if (!imu) { - kfree(node); + io_cache_free(&ctx->node_cache, node); ret = -ENOMEM; goto unlock; } -- 2.43.0 From: Koba Ko io_import_umem() has two problems: 1. When io_account_mem() fails, the function returns an error but leaves live pinned pages and sg_table in the mem struct without cleaning them up. The caller happens to handle this today via io_zcrx_free_area() -> io_release_area_mem(), but the contract is fragile. 2. io_release_area_mem() doesn't NULL out mem->pages after kvfree(), making it unsafe to call twice. Since io_zcrx_free_area() always calls it during teardown, any earlier cleanup call would cause a double-free. Fix both: populate mem fields before io_account_mem() so io_release_area_mem() can do a proper cleanup on failure, and add mem->pages = NULL in io_release_area_mem() to make it idempotent. Fixes: 262ab205180d2 ("io_uring/zcrx: account area memory") Signed-off-by: Koba Ko --- io_uring/zcrx.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 62d693287457f..c9ed1139c7bcd 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -188,6 +188,8 @@ static unsigned long io_count_account_pages(struct page **pages, unsigned nr_pag return res; } +static void io_release_area_mem(struct io_zcrx_mem *mem); + static int io_import_umem(struct io_zcrx_ifq *ifq, struct io_zcrx_mem *mem, struct io_uring_zcrx_area_reg *area_reg) @@ -213,16 +215,20 @@ static int io_import_umem(struct io_zcrx_ifq *ifq, return ret; } - mem->account_pages = io_count_account_pages(pages, nr_pages); - ret = io_account_mem(ifq->user, ifq->mm_account, mem->account_pages); - if (ret < 0) - mem->account_pages = 0; - mem->sgt = &mem->page_sg_table; mem->pages = pages; mem->nr_folios = nr_pages; mem->size = area_reg->len; - return ret; + + mem->account_pages = io_count_account_pages(pages, nr_pages); + ret = io_account_mem(ifq->user, ifq->mm_account, mem->account_pages); + if (ret < 0) { + mem->account_pages = 0; + io_release_area_mem(mem); + return ret; + } + + return 0; } static void io_release_area_mem(struct io_zcrx_mem *mem) @@ -236,6 +242,7 @@ static void io_release_area_mem(struct io_zcrx_mem *mem) sg_free_table(mem->sgt); mem->sgt = NULL; kvfree(mem->pages); + mem->pages = NULL; } } -- 2.43.0