From eb8ab8c23e71c6ae77dedc45de2a57a61558e5d5 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 29 Oct 2026 11:34:49 +0100 Subject: [PATCH] iomap: keep large chunks from defeating the bio cap For integrity I/O a bio must not grow beyond max_integrity_io_size() because the integrity buffer is a single allocation of at most 2 MiB. Writeback and buffered reads check that before adding to a bio: bio->bi_iter.bi_size > iomap_max_bio_size(iomap) - len The subtraction is done in size_t and wraps if len alone is above the limit. The check then never fires and the bio grows until it runs out of bio_vecs. That's easy to hit. The limit is also capped by the maximum segment size which is 64 KiB for SCSI hosts that don't set it, such as lpfc and qla2xxx, and XFS uses folios of up to 2 MiB. Writeback of a preallocated 300 MiB file then builds a single bio that needs 4.7 MiB of PI with 512 byte sectors and 8 byte tuples. That's more than kmalloc() can do, so bio_integrity_alloc_buf() falls back to a 2 MiB mempool element and generating the PI writes 2.7 MiB past its end. Check the length on its own first. A chunk above the limit still gets a bio of its own which is never larger than a folio. Fixes: 0b10a370529c ("iomap: support T10 protection information") Cc: stable@vger.kernel.org # v7.1+ Signed-off-by: Josef Bacik Link: https://patch.msgid.link/20261029-work-iomap-ai-fixes-v1-2-7eda63bc1b13@kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/bio.c | 2 +- fs/iomap/internal.h | 9 +++++++++ fs/iomap/ioend.c | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 153b779949e9..256564ebe238 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -109,7 +109,7 @@ int iomap_bio_read_folio_range(const struct iomap_iter *iter, if (!bio || bio_end_sector(bio) != iomap_sector(&iter->iomap, iter->pos) || - bio->bi_iter.bi_size > iomap_max_bio_size(&iter->iomap) - plen || + !iomap_bio_has_room(&iter->iomap, bio, plen) || !bio_add_folio(bio, folio, plen, offset_in_folio(folio, iter->pos))) iomap_read_alloc_bio(iter, ctx, plen); return 0; diff --git a/fs/iomap/internal.h b/fs/iomap/internal.h index 74e898b196dc..e69b3ac02cc2 100644 --- a/fs/iomap/internal.h +++ b/fs/iomap/internal.h @@ -17,6 +17,15 @@ static inline size_t iomap_max_bio_size(const struct iomap *iomap) return BIO_MAX_SIZE; } +/* Can @len more bytes go into @bio? */ +static inline bool iomap_bio_has_room(const struct iomap *iomap, + const struct bio *bio, size_t len) +{ + size_t max_size = iomap_max_bio_size(iomap); + + return len <= max_size && bio->bi_iter.bi_size <= max_size - len; +} + u32 iomap_finish_ioend_buffered_read(struct iomap_ioend *ioend); u32 iomap_finish_ioend_direct(struct iomap_ioend *ioend); diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 92b89fbf95b9..4da248ff99f8 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -133,8 +133,7 @@ static bool iomap_can_add_to_ioend(struct iomap_writepage_ctx *wpc, loff_t pos, { struct iomap_ioend *ioend = wpc->wb_ctx; - if (ioend->io_bio.bi_iter.bi_size > - iomap_max_bio_size(&wpc->iomap) - map_len) + if (!iomap_bio_has_room(&wpc->iomap, &ioend->io_bio, map_len)) return false; if (ioend_flags & IOMAP_IOEND_BOUNDARY) return false;