io_uring/rsrc.c | 1 + 1 file changed, 1 insertion(+)
io_import_kbuf() calculates nr_segs incorrectly when iov_offset is
non-zero after iov_iter_advance(). It doesn't account for the partial
consumption of the first bvec.
The problem comes when meet the following conditions:
1. Use UBLK_F_AUTO_BUF_REG feature of ublk.
2. The kernel will help to register the buffer, into the io uring.
3. Later, the ublk server try to send IO request using the registered
buffer in the io uring, to read/write to fuse-based filesystem, with
O_DIRECT.
From a userspace perspective, the ublk server thread is blocked in the
kernel, and will see "soft lockup" in the kernel dmesg.
When ublk registers a buffer with mixed-size bvecs like [4K]*6 + [12K]
and a request partially consumes a bvec, the next request's nr_segs
calculation uses bvec->bv_len instead of (bv_len - iov_offset).
This causes fuse_get_user_pages() to loop forever because nr_segs
indicates fewer pages than actually needed.
Specifically, the infinite loop happens at:
fuse_get_user_pages()
-> iov_iter_extract_pages()
-> iov_iter_extract_bvec_pages()
Since the nr_segs is miscalculated, the iov_iter_extract_bvec_pages
returns when finding that i->nr_segs is zero. Then
iov_iter_extract_pages returns zero. However, fuse_get_user_pages does
still not get enough data/pages, causing infinite loop.
Example:
- Bvecs: [4K, 4K, 4K, 4K, 4K, 4K, 12K, ...]
- Request 1: 32K at offset 0, uses 6*4K + 8K of the 12K bvec
- Request 2: 32K at offset 32K
- iov_offset = 8K (8K already consumed from 12K bvec)
- Bug: calculates using 12K, not (12K - 8K) = 4K
- Result: nr_segs too small, infinite loop in fuse_get_user_pages.
Fix by accounting for iov_offset when calculating the first segment's
available length.
Fixes: b419bed4f0a6 ("io_uring/rsrc: ensure segments counts are correct on kbuf buffers")
Signed-off-by: huang-jl <huang-jl@deepseek.com>
---
v2: Optimize the logic to handle the iov_offset and add Fixes tag.
> Please add a Fixes tag
Thanks for your notice, this is my first time to send patch to linux. I
have add the Fixes tag, but not sure if I am doing it correctly.
> Would a simpler fix be just to add a len += iter->iov_offset before the loop?
Great suggestion! I have tried it, and also fix the bug correctly.
io_uring/rsrc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index a63474b331bf..41c89f5c616d 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -1059,6 +1059,7 @@ static int io_import_kbuf(int ddir, struct iov_iter *iter,
if (count < imu->len) {
const struct bio_vec *bvec = iter->bvec;
+ len += iter->iov_offset;
while (len > bvec->bv_len) {
len -= bvec->bv_len;
bvec++;
--
2.43.0
On Tue, Dec 16, 2025 at 10:27 PM huang-jl <huang-jl@deepseek.com> wrote:
>
> io_import_kbuf() calculates nr_segs incorrectly when iov_offset is
> non-zero after iov_iter_advance(). It doesn't account for the partial
> consumption of the first bvec.
>
> The problem comes when meet the following conditions:
> 1. Use UBLK_F_AUTO_BUF_REG feature of ublk.
> 2. The kernel will help to register the buffer, into the io uring.
> 3. Later, the ublk server try to send IO request using the registered
> buffer in the io uring, to read/write to fuse-based filesystem, with
> O_DIRECT.
>
> From a userspace perspective, the ublk server thread is blocked in the
> kernel, and will see "soft lockup" in the kernel dmesg.
>
> When ublk registers a buffer with mixed-size bvecs like [4K]*6 + [12K]
> and a request partially consumes a bvec, the next request's nr_segs
> calculation uses bvec->bv_len instead of (bv_len - iov_offset).
>
> This causes fuse_get_user_pages() to loop forever because nr_segs
> indicates fewer pages than actually needed.
>
> Specifically, the infinite loop happens at:
> fuse_get_user_pages()
> -> iov_iter_extract_pages()
> -> iov_iter_extract_bvec_pages()
> Since the nr_segs is miscalculated, the iov_iter_extract_bvec_pages
> returns when finding that i->nr_segs is zero. Then
> iov_iter_extract_pages returns zero. However, fuse_get_user_pages does
> still not get enough data/pages, causing infinite loop.
>
> Example:
> - Bvecs: [4K, 4K, 4K, 4K, 4K, 4K, 12K, ...]
> - Request 1: 32K at offset 0, uses 6*4K + 8K of the 12K bvec
> - Request 2: 32K at offset 32K
> - iov_offset = 8K (8K already consumed from 12K bvec)
> - Bug: calculates using 12K, not (12K - 8K) = 4K
> - Result: nr_segs too small, infinite loop in fuse_get_user_pages.
>
> Fix by accounting for iov_offset when calculating the first segment's
> available length.
>
> Fixes: b419bed4f0a6 ("io_uring/rsrc: ensure segments counts are correct on kbuf buffers")
> Signed-off-by: huang-jl <huang-jl@deepseek.com>
> ---
> v2: Optimize the logic to handle the iov_offset and add Fixes tag.
>
> > Please add a Fixes tag
>
> Thanks for your notice, this is my first time to send patch to linux. I
> have add the Fixes tag, but not sure if I am doing it correctly.
Yup, that looks great. That will help figure out which stable kernels
the patch should be backported to.
Thanks,
Caleb
>
> > Would a simpler fix be just to add a len += iter->iov_offset before the loop?
>
> Great suggestion! I have tried it, and also fix the bug correctly.
>
> io_uring/rsrc.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
> index a63474b331bf..41c89f5c616d 100644
> --- a/io_uring/rsrc.c
> +++ b/io_uring/rsrc.c
> @@ -1059,6 +1059,7 @@ static int io_import_kbuf(int ddir, struct iov_iter *iter,
> if (count < imu->len) {
> const struct bio_vec *bvec = iter->bvec;
>
> + len += iter->iov_offset;
> while (len > bvec->bv_len) {
> len -= bvec->bv_len;
> bvec++;
> --
> 2.43.0
>
On Wed, 17 Dec 2025 14:26:32 +0800, huang-jl wrote:
> io_import_kbuf() calculates nr_segs incorrectly when iov_offset is
> non-zero after iov_iter_advance(). It doesn't account for the partial
> consumption of the first bvec.
>
> The problem comes when meet the following conditions:
> 1. Use UBLK_F_AUTO_BUF_REG feature of ublk.
> 2. The kernel will help to register the buffer, into the io uring.
> 3. Later, the ublk server try to send IO request using the registered
> buffer in the io uring, to read/write to fuse-based filesystem, with
> O_DIRECT.
>
> [...]
Applied, thanks!
[1/1] io_uring: fix nr_segs calculation in io_import_kbuf
commit: 114ea9bbaf7681c4d363e13b7916e6fef6a4963a
Best regards,
--
Jens Axboe
On Wed, Dec 17, 2025 at 02:26:32PM +0800, huang-jl wrote:
> io_import_kbuf() calculates nr_segs incorrectly when iov_offset is
> non-zero after iov_iter_advance(). It doesn't account for the partial
> consumption of the first bvec.
>
> The problem comes when meet the following conditions:
> 1. Use UBLK_F_AUTO_BUF_REG feature of ublk.
> 2. The kernel will help to register the buffer, into the io uring.
> 3. Later, the ublk server try to send IO request using the registered
> buffer in the io uring, to read/write to fuse-based filesystem, with
> O_DIRECT.
>
> From a userspace perspective, the ublk server thread is blocked in the
> kernel, and will see "soft lockup" in the kernel dmesg.
>
> When ublk registers a buffer with mixed-size bvecs like [4K]*6 + [12K]
> and a request partially consumes a bvec, the next request's nr_segs
> calculation uses bvec->bv_len instead of (bv_len - iov_offset).
>
> This causes fuse_get_user_pages() to loop forever because nr_segs
> indicates fewer pages than actually needed.
>
> Specifically, the infinite loop happens at:
> fuse_get_user_pages()
> -> iov_iter_extract_pages()
> -> iov_iter_extract_bvec_pages()
> Since the nr_segs is miscalculated, the iov_iter_extract_bvec_pages
> returns when finding that i->nr_segs is zero. Then
> iov_iter_extract_pages returns zero. However, fuse_get_user_pages does
> still not get enough data/pages, causing infinite loop.
>
> Example:
> - Bvecs: [4K, 4K, 4K, 4K, 4K, 4K, 12K, ...]
> - Request 1: 32K at offset 0, uses 6*4K + 8K of the 12K bvec
> - Request 2: 32K at offset 32K
> - iov_offset = 8K (8K already consumed from 12K bvec)
> - Bug: calculates using 12K, not (12K - 8K) = 4K
> - Result: nr_segs too small, infinite loop in fuse_get_user_pages.
>
> Fix by accounting for iov_offset when calculating the first segment's
> available length.
>
> Fixes: b419bed4f0a6 ("io_uring/rsrc: ensure segments counts are correct on kbuf buffers")
> Signed-off-by: huang-jl <huang-jl@deepseek.com>
> ---
> v2: Optimize the logic to handle the iov_offset and add Fixes tag.
>
> > Please add a Fixes tag
>
> Thanks for your notice, this is my first time to send patch to linux. I
> have add the Fixes tag, but not sure if I am doing it correctly.
>
> > Would a simpler fix be just to add a len += iter->iov_offset before the loop?
>
> Great suggestion! I have tried it, and also fix the bug correctly.
>
> io_uring/rsrc.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
> index a63474b331bf..41c89f5c616d 100644
> --- a/io_uring/rsrc.c
> +++ b/io_uring/rsrc.c
> @@ -1059,6 +1059,7 @@ static int io_import_kbuf(int ddir, struct iov_iter *iter,
> if (count < imu->len) {
> const struct bio_vec *bvec = iter->bvec;
>
> + len += iter->iov_offset;
> while (len > bvec->bv_len) {
> len -= bvec->bv_len;
> bvec++;
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Thanks,
Ming
© 2016 - 2025 Red Hat, Inc.