diff options
author | Gabriel Krisman Bertazi <krisman@suse.de> | 2024-03-13 17:39:12 -0400 |
---|---|---|
committer | Sasha Levin <sashal@kernel.org> | 2024-03-26 18:18:54 -0400 |
commit | 712e2c8415f55a4a4ddaa98a430b87f624109f69 (patch) | |
tree | 0873096930a3fd3d1b1c8458f8c0a1f229c8c4f9 /io_uring | |
parent | 25cc5521865a6b273ae694448b92a236b1785391 (diff) | |
download | linux-stable-712e2c8415f55a4a4ddaa98a430b87f624109f69.tar.gz linux-stable-712e2c8415f55a4a4ddaa98a430b87f624109f69.tar.bz2 linux-stable-712e2c8415f55a4a4ddaa98a430b87f624109f69.zip |
io_uring: Fix release of pinned pages when __io_uaddr_map fails
[ Upstream commit 67d1189d1095d471ed7fa426c7e384a7140a5dd7 ]
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: Gabriel Krisman Bertazi <krisman@suse.de>
Fixes: 223ef4743164 ("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: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'io_uring')
-rw-r--r-- | io_uring/io_uring.c | 22 |
1 files changed, 13 insertions, 9 deletions
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 205dad960373..45d6e440bdc0 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2683,7 +2683,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; @@ -2697,12 +2697,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, - 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); + + pinned = pin_user_pages_fast(uaddr, nr_pages, FOLL_WRITE | FOLL_LONGTERM, + page_array); + if (pinned != nr_pages) { + ret = (pinned < 0) ? pinned : -EFAULT; + goto free_pages; } page_addr = page_address(page_array[0]); @@ -2716,7 +2716,7 @@ err: * 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 @@ -2725,13 +2725,17 @@ err: * 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, |