[PATCH] block: fix pages array leak in bio_map_user_iov error path

Xin Yin posted 1 patch 1 month ago
block/blk-map.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH] block: fix pages array leak in bio_map_user_iov error path
Posted by Xin Yin 1 month ago
In bio_map_user_iov(), when iov_iter_extract_pages() is called with pages
set to NULL (because nr_vecs > UIO_FASTIOV), want_pages_array() internally
allocates a pages array via kvmalloc_array(). If iov_iter_extract_pages()
subsequently returns bytes <= 0 (e.g., due to pin_user_pages_fast()
failure), the code jumps to out_unmap without freeing the dynamically
allocated pages array, causing a memory leak detectable by kmemleak.

This can be triggered from userspace by issuing an SG_IO v4 ioctl on a
bsg device with a large din_xfer_len and an invalid din_xferp (mapped
PROT_NONE), which causes pin_user_pages_fast() to fail after the pages
array has already been allocated by want_pages_array().

The kmemleak backtrace looks like:

  unreferenced object 0xffff... (size 2048):
    backtrace (crc 0):
      kvmalloc_node+0x...
      want_pages_array+0x...
      iov_iter_extract_pages+0x...
      bio_map_user_iov+0x...
      blk_rq_map_user_iov+0x...
      blk_rq_map_user+0x...
      bsg_transport_sg_io_fn+0x...

Fix this by freeing the dynamically allocated pages array (when it
differs from the on-stack stack_pages) before jumping to the error path.

Fixes: 403b6fb8dac1 ("block: convert bio_map_user_iov to use iov_iter_extract_pages")
Cc: stable@vger.kernel.org # 6.5+
Signed-off-by: Xin Yin <yinxin.x@bytedance.com>
---
 block/blk-map.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/block/blk-map.c b/block/blk-map.c
index 0aadbaf7a9ddd..5b9f14caad4f9 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -304,6 +304,8 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
 		bytes = iov_iter_extract_pages(iter, &pages, LONG_MAX,
 					       nr_vecs, extraction_flags, &offs);
 		if (unlikely(bytes <= 0)) {
+			if (pages != stack_pages)
+				kvfree(pages);
 			ret = bytes ? bytes : -EFAULT;
 			goto out_unmap;
 		}
-- 
2.20.1
Re: [PATCH] block: fix pages array leak in bio_map_user_iov error path
Posted by 尹欣 3 weeks, 4 days ago
> From: "Xin Yin"<yinxin.x@bytedance.com>
> Date:  Sat, May 9, 2026, 12:23
> Subject:  [PATCH] block: fix pages array leak in bio_map_user_iov error path
> To: <axboe@kernel.dk>, <dhowells@redhat.com>
> Cc: <linux-block@vger.kernel.org>, <linux-kernel@vger.kernel.org>, "Xin Yin"<yinxin.x@bytedance.com>, <stable@vger.kernel.org>
> In bio_map_user_iov(), when iov_iter_extract_pages() is called with pages
> set to NULL (because nr_vecs > UIO_FASTIOV), want_pages_array() internally
> allocates a pages array via kvmalloc_array(). If iov_iter_extract_pages()
> subsequently returns bytes <= 0 (e.g., due to pin_user_pages_fast()
> failure), the code jumps to out_unmap without freeing the dynamically
> allocated pages array, causing a memory leak detectable by kmemleak.
> 
> This can be triggered from userspace by issuing an SG_IO v4 ioctl on a
> bsg device with a large din_xfer_len and an invalid din_xferp (mapped
> PROT_NONE), which causes pin_user_pages_fast() to fail after the pages
> array has already been allocated by want_pages_array().
> 
> The kmemleak backtrace looks like:
> 
>   unreferenced object 0xffff... (size 2048):
>     backtrace (crc 0):
>       kvmalloc_node+0x...
>       want_pages_array+0x...
>       iov_iter_extract_pages+0x...
>       bio_map_user_iov+0x...
>       blk_rq_map_user_iov+0x...
>       blk_rq_map_user+0x...
>       bsg_transport_sg_io_fn+0x...
> 
> Fix this by freeing the dynamically allocated pages array (when it
> differs from the on-stack stack_pages) before jumping to the error path.
> 
> Fixes: 403b6fb8dac1 ("block: convert bio_map_user_iov to use iov_iter_extract_pages")
> Cc: stable@vger.kernel.org # 6.5+
> Signed-off-by: Xin Yin <yinxin.x@bytedance.com>
> ---
>  block/blk-map.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/block/blk-map.c b/block/blk-map.c
> index 0aadbaf7a9ddd..5b9f14caad4f9 100644
> --- a/block/blk-map.c
> +++ b/block/blk-map.c
> @@ -304,6 +304,8 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
>                  bytes = iov_iter_extract_pages(iter, &pages, LONG_MAX,
>                                                 nr_vecs, extraction_flags, &offs);
>                  if (unlikely(bytes <= 0)) {
> +                        if (pages != stack_pages)
> +                                kvfree(pages);
>                          ret = bytes ? bytes : -EFAULT;
>                          goto out_unmap;
>                  }
> -- 
> 2.20.1
>
Hi Jens

Gentle ping on this patch.

This patch fixes a memory leak in bio_map_user_iov() that only affects
stable kernels (6.5+) carrying commit 403b6fb8dac1. Mainline resolved
this through a later refactoring that removed the affected code path
entirely, so there is no upstream commit to backport.

Could this be picked up for 6.6.y?

Thanks,
Xin Yin