On 3/17/26 11:35, Konstantin Khorenko wrote:
> ploop_pio_nr_segs() used for_each_bvec() to count the number of
> segments in a pio. for_each_bvec() iterates via bvec_iter_bvec()
> which clamps each segment to a single page boundary, so a single
> multi-page bvec entry would be counted as multiple page-sized
> segments. This overcounted nr_segs was then passed to iov_iter_bvec()
> in ploop_submit_rw_mapped().
> 
> iov_iter_bvec_advance() uses nr_segs as the bvec array bound
> (end = bvec + i->nr_segs), iterating one full bvec entry per step.
> With an inflated nr_segs it reads past the end of the bvec array,
> causing the KASAN slab-out-of-bounds in iov_iter_advance().
> 
> Fix by replacing for_each_bvec() with a manual loop that counts
> actual bvec array entries, advancing through whole bvec entries
> (respecting each entry's full bv_len) instead of page-sized
> sub-segments.
> 
> Fixes: 0a54bd2238a16 ("dm-ploop: Add ploop target driver")
> https://virtuozzo.atlassian.net/browse/VSTOR-126957
> 
> Signed-off-by: Konstantin Khorenko <[email protected]>
> 
> Feature: dm-ploop: ploop target driver
> ---
>  drivers/md/dm-ploop-map.c | 24 +++++++++++++++---------
>  1 file changed, 15 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/md/dm-ploop-map.c b/drivers/md/dm-ploop-map.c
> index 2739cc3f09028..f8361640cefc6 100644
> --- a/drivers/md/dm-ploop-map.c
> +++ b/drivers/md/dm-ploop-map.c
> @@ -36,18 +36,24 @@ extern struct static_key_false ploop_standby_check;
>  
>  static unsigned int ploop_pio_nr_segs(struct pio *pio)
>  {
> -     struct bvec_iter bi = {
> -             .bi_size = pio->bi_iter.bi_size,
> -             .bi_bvec_done = pio->bi_iter.bi_bvec_done,
> -             .bi_idx = pio->bi_iter.bi_idx,
> -     };
>       unsigned int nr_segs = 0;
> -     struct bio_vec bv;
> +     unsigned int bytes = pio->bi_iter.bi_size;
> +     unsigned int idx = pio->bi_iter.bi_idx;
> +     unsigned int done = pio->bi_iter.bi_bvec_done;
> +     unsigned int avail;
>  
> -     for_each_bvec(bv, pio->bi_io_vec, bi, bi)
> -                nr_segs++;
> +     while (bytes > 0) {
> +             avail = pio->bi_io_vec[idx].bv_len - done;
> +
> +             if (avail > bytes)
> +                     avail = bytes;
> +             bytes -= avail;
> +             idx++;
> +             done = 0;
> +             nr_segs++;
> +     }

Can we, maybe, reuse qcow2_for_each_bvec? See how it's used in qio_nr_segs to 
count nr_segs. E.g.:

qcow2_for_each_bvec(iter, bv, pio->bi_iter, pio->bi_io_vec)
    nr_segs++;

I see qcow2_for_each_bvec uses bvec_iter_advance, instead of 
bvec_iter_advance_single used in for_each_bvec, so it should count segs indeed.

>  
> -        return nr_segs;
> +     return nr_segs;
>  }
>  
>  static sector_t ploop_rq_pos(struct ploop *ploop, struct request *rq)

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to