Re: [PATCH RFC 3/5] block: validate bio bounds in the queue entered context

Chris S <[email protected]>
Newsgroups org.kernel.vger.linux-block,org.infradead.lists.linux-nvme
Message-ID <CACd_6n2jNS9Msd_N-idPx3ijgN8NUxT1U1m2OrJu=e+GToB+ZA@mail.gmail.com>
On 2026/05/20 9:25, Damien Le Moal wrote:
>> Should this be a pr_err_ratelimited() ?

FWIW, I think pr_info is deliberate and worth keeping.  The comment this
patch removes together with bio_check_eod() gave the reason:

  /*
   * Check whether this bio extends beyond the end of the device or
   * partition.  This may well happen - the kernel calls bread() without
   * checking the size of the device, e.g., when mounting a file system.
   */

So it fires on a legitimate and expected pattern, and pr_err would be
noisy for it.  It may be worth carrying that comment over to the new
location so the reason for the level does not get lost.

Keith, I checked this patch against the nvme_setup_rw() WARN I reported
(the second link in your cover letter).  __bio_split_to_limits() is
called from blk_mq_submit_bio() after bio_queue_enter(), on both the
cached-request path and the newly-allocated-request path, so the bounds
check is now serialized against the freeze window in
nvme_update_ns_info_block().  With the capacity forced to 0, the bio is
failed here and can no longer reach nvme_queue_rq(), which is exactly
the race I hit.  Thanks for picking it up, and sorry for the slow reply.

I'll drop my own "nvme: don't WARN on I/O to a namespace revalidated to
unusable metadata" patch in favour of this.

Reviewed-by: Chao Shi <[email protected]>

Best,
Chao

On Tue, May 19, 2026 at 1:23 PM Keith Busch <[email protected]> wrote:
>
> From: Keith Busch <[email protected]>
>
> bio_check_eod() in submit_bio_noacct() validates that a bio does not
> extend beyond the partition's available sectors. This check runs before
> bio_queue_enter(), so it is not serialized against queue limit updates.
> A driver that freezes the queue, updates limits, changes the capacity,
> and unfreezes can race with a bio that passed the early check under the
> old capacity.
>
> Remove bio_check_eod() and replace it with a bounds check in
> __bio_split_to_limits(), which runs after the queue usage reference has
> been acquired. The check uses partition-aware arithmetic to validate
> both partition bounds and disk capacity in a single comparison that
> works correctly on the post-remap sector values.
>
> Signed-off-by: Keith Busch <[email protected]>
> ---
>  block/blk-core.c | 26 --------------------------
>  block/blk.h      | 14 ++++++++++++++
>  2 files changed, 14 insertions(+), 26 deletions(-)
>
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 92a802dc8042c..c200d0fc44fe7 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -547,30 +547,6 @@ int should_fail_bio(struct bio *bio)
>  }
>  ALLOW_ERROR_INJECTION(should_fail_bio, ERRNO);
>
> -/*
> - * Check whether this bio extends beyond the end of the device or partition.
> - * This may well happen - the kernel calls bread() without checking the size of
> - * the device, e.g., when mounting a file system.
> - */
> -static inline int bio_check_eod(struct bio *bio)
> -{
> -       sector_t maxsector = bdev_nr_sectors(bio->bi_bdev);
> -       unsigned int nr_sectors = bio_sectors(bio);
> -
> -       if (nr_sectors &&
> -           (nr_sectors > maxsector ||
> -            bio->bi_iter.bi_sector > maxsector - nr_sectors)) {
> -               if (!maxsector)
> -                       return -EIO;
> -               pr_info_ratelimited("%s: attempt to access beyond end of device\n"
> -                                   "%pg: rw=%d, sector=%llu, nr_sectors = %u limit=%llu\n",
> -                                   current->comm, bio->bi_bdev, bio->bi_opf,
> -                                   bio->bi_iter.bi_sector, nr_sectors, maxsector);
> -               return -EIO;
> -       }
> -       return 0;
> -}
> -
>  /*
>   * Remap block n of partition p to block n+start(p) of the disk.
>   */
> @@ -802,8 +778,6 @@ void submit_bio_noacct(struct bio *bio)
>                 goto end_io;
>         bio_check_ro(bio);
>         if (!bio_flagged(bio, BIO_REMAPPED)) {
> -               if (unlikely(bio_check_eod(bio)))
> -                       goto end_io;
>                 if (bdev_is_partition(bdev) &&
>                     unlikely(blk_partition_remap(bio)))
>                         goto end_io;
> diff --git a/block/blk.h b/block/blk.h
> index bf1a80493ff1c..e70acb2d358e3 100644
> --- a/block/blk.h
> +++ b/block/blk.h
> @@ -423,6 +423,17 @@ static inline bool bio_may_need_split(struct bio *bio,
>  static inline struct bio *__bio_split_to_limits(struct bio *bio,
>                 const struct queue_limits *lim, unsigned int *nr_segs)
>  {
> +       if (unlikely(bio_end_sector(bio) > bdev_nr_sectors(bio->bi_bdev) +
> +                                          bio->bi_bdev->bd_start_sect)) {
> +               pr_info_ratelimited("%s: attempt to access beyond end of device\n"
> +                                   "%pg: rw=%d, sector=%llu, nr_sectors = %u limit=%llu\n",
> +                                   current->comm, bio->bi_bdev, bio->bi_opf,
> +                                   bio->bi_iter.bi_sector, bio_sectors(bio),
> +                                   bdev_nr_sectors(bio->bi_bdev) +
> +                                       bio->bi_bdev->bd_start_sect);
> +               goto ioerr;
> +       }
> +
>         switch (bio_op(bio)) {
>         case REQ_OP_READ:
>         case REQ_OP_WRITE:
> @@ -442,6 +453,9 @@ static inline struct bio *__bio_split_to_limits(struct bio *bio,
>                 *nr_segs = 0;
>                 return bio;
>         }
> +ioerr:
> +       bio_io_error(bio);
> +       return NULL;
>  }
>
>  /**
> --
> 2.53.0-Meta
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.