Commit 67d1189d authored by Gabriel Krisman Bertazi's avatar Gabriel Krisman Bertazi Committed by Jens Axboe
Browse files

io_uring: Fix release of pinned pages when __io_uaddr_map fails



Looking at the error path of __io_uaddr_map, if we fail after pinning
the pages for any reasons, ret will be set to -EINVAL and the error
handler won't properly release the pinned pages.

I didn't manage to trigger it without forcing a failure, but it can
happen in real life when memory is heavily fragmented.

Signed-off-by: default avatarGabriel Krisman Bertazi <krisman@suse.de>
Fixes: 223ef474 ("io_uring: don't allow IORING_SETUP_NO_MMAP rings on highmem pages")
Link: https://lore.kernel.org/r/20240313213912.1920-1-krisman@suse.de


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 9219e4a9
Loading
Loading
Loading
Loading
+13 −9
Original line number Diff line number Diff line
@@ -2714,7 +2714,7 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages,
	struct page **page_array;
	unsigned int nr_pages;
	void *page_addr;
	int ret, i;
	int ret, i, pinned;

	*npages = 0;

@@ -2728,12 +2728,12 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages,
	if (!page_array)
		return ERR_PTR(-ENOMEM);

	ret = pin_user_pages_fast(uaddr, nr_pages, FOLL_WRITE | FOLL_LONGTERM,

	pinned = pin_user_pages_fast(uaddr, nr_pages, FOLL_WRITE | FOLL_LONGTERM,
				     page_array);
	if (ret != nr_pages) {
err:
		io_pages_free(&page_array, ret > 0 ? ret : 0);
		return ret < 0 ? ERR_PTR(ret) : ERR_PTR(-EFAULT);
	if (pinned != nr_pages) {
		ret = (pinned < 0) ? pinned : -EFAULT;
		goto free_pages;
	}

	page_addr = page_address(page_array[0]);
@@ -2747,7 +2747,7 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages,
		 * didn't support this feature.
		 */
		if (PageHighMem(page_array[i]))
			goto err;
			goto free_pages;

		/*
		 * No support for discontig pages for now, should either be a
@@ -2756,13 +2756,17 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages,
		 * just fail them with EINVAL.
		 */
		if (page_address(page_array[i]) != page_addr)
			goto err;
			goto free_pages;
		page_addr += PAGE_SIZE;
	}

	*pages = page_array;
	*npages = nr_pages;
	return page_to_virt(page_array[0]);

free_pages:
	io_pages_free(&page_array, pinned > 0 ? pinned : 0);
	return ERR_PTR(ret);
}

static void *io_rings_map(struct io_ring_ctx *ctx, unsigned long uaddr,