From 842ed2ab1c9821856a77e64403aa7dffb5c8d795 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 4 Nov 2019 17:30:09 +0800 Subject: [PATCH] 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 3d757055305f..f4ead66b0b6f 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 9decd10facc6..6819e9858ea5 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -338,7 +338,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data, 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 b697866946d2..3ca43a1f3916 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -620,7 +620,7 @@ xfs_add_to_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 c7433a201171..8a6f4c73b25c 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; } /* -- GitLab