[PATCH RFC 3/5] block: validate bio bounds in the queue entered context
Keith Busch
kbusch at meta.com
Tue May 19 10:23:24 PDT 2026
From: Keith Busch <kbusch at kernel.org>
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 <kbusch at kernel.org>
---
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
More information about the Linux-nvme
mailing list