From 842ed2ab1c9821856a77e64403aa7dffb5c8d795 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 4 Nov 2019 17:30:09 +0800 Subject: [PATCH 0182/2944] block: fix .bi_size overflow commit 79d08f89bb1b5c2c1ff90d9bb95497ab9e8aa7e0 upstream 'bio->bi_iter.bi_size' is 'unsigned int', which at most hold 4G - 1 bytes. Before 07173c3ec276 ("block: enable multipage bvecs"), one bio can include very limited pages, and usually at most 256, so the fs bio size won't be bigger than 1M bytes most of times. Since we support multi-page bvec, in theory one fs bio really can be added > 1M pages, especially in case of hugepage, or big writeback with too many dirty pages. Then there is chance in which .bi_size is overflowed. Fixes this issue by using bio_full() to check if the added segment may overflow .bi_size. Signed-off-by: Hui Zhu Cc: Liu Yiding Cc: kernel test robot Cc: "Darrick J. Wong" Cc: linux-xfs@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: 07173c3ec276 ("block: enable multipage bvecs") Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe Reviewed-by: Joseph Qi --- block/bio.c | 8 ++++---- fs/iomap.c | 2 +- fs/xfs/xfs_aops.c | 2 +- include/linux/bio.h | 18 ++++++++++++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/block/bio.c b/block/bio.c index 3d75705..f4ead66 100644 --- a/block/bio.c +++ b/block/bio.c @@ -700,7 +700,7 @@ int bio_add_pc_page(struct request_queue *q, struct bio *bio, struct page return 0; } - if (bio_full(bio)) + if (bio_full(bio, len)) return 0; /* @@ -795,7 +795,7 @@ void __bio_add_page(struct bio *bio, struct page *page, struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt]; WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); - WARN_ON_ONCE(bio_full(bio)); + WARN_ON_ONCE(bio_full(bio, len)); bv->bv_page = page; bv->bv_offset = off; @@ -820,7 +820,7 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int offset) { if (!__bio_try_merge_page(bio, page, len, offset)) { - if (bio_full(bio)) + if (bio_full(bio, len)) return 0; __bio_add_page(bio, page, len, offset); } @@ -898,7 +898,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) if (unlikely(ret)) return bio->bi_vcnt > orig_vcnt ? 0 : ret; - } while (iov_iter_count(iter) && !bio_full(bio)); + } while (iov_iter_count(iter) && !bio_full(bio, 0)); return 0; } diff --git a/fs/iomap.c b/fs/iomap.c index 9decd10..6819e98 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -338,7 +338,7 @@ struct iomap_readpage_ctx { if (iop) atomic_inc(&iop->read_count); - if (!ctx->bio || !is_contig || bio_full(ctx->bio)) { + if (!ctx->bio || !is_contig || bio_full(ctx->bio, plen)) { gfp_t gfp = mapping_gfp_constraint(page->mapping, GFP_KERNEL); int nr_vecs = (length + PAGE_SIZE - 1) >> PAGE_SHIFT; diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index b697866..3ca43a1 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -620,7 +620,7 @@ static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend) if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff)) { if (iop) atomic_inc(&iop->write_count); - if (bio_full(wpc->ioend->io_bio)) + if (bio_full(wpc->ioend->io_bio, len)) xfs_chain_bio(wpc->ioend, wbc, bdev, sector); __bio_add_page(wpc->ioend->io_bio, page, len, poff); } diff --git a/include/linux/bio.h b/include/linux/bio.h index c7433a2..8a6f4c7 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -127,9 +127,23 @@ static inline void *bio_data(struct bio *bio) return NULL; } -static inline bool bio_full(struct bio *bio) +/** + * bio_full - check if the bio is full + * @bio: bio to check + * @len: length of one segment to be added + * + * Return true if @bio is full and one segment with @len bytes can't be + * added to the bio, otherwise return false + */ +static inline bool bio_full(struct bio *bio, unsigned len) { - return bio->bi_vcnt >= bio->bi_max_vecs; + if (bio->bi_vcnt >= bio->bi_max_vecs) + return true; + + if (bio->bi_iter.bi_size > UINT_MAX - len) + return true; + + return false; } /* -- 1.8.3.1