]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
block: fix .bi_size overflow
authorMing Lei <ming.lei@redhat.com>
Mon, 1 Jul 2019 07:14:46 +0000 (15:14 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 14 Jul 2019 06:01:08 +0000 (08:01 +0200)
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.

Cc: Liu Yiding <liuyd.fnst@cn.fujitsu.com>
Cc: kernel test robot <rong.a.chen@intel.com>
Cc: "Darrick J. Wong" <darrick.wong@oracle.com>
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 <hch@lst.de>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
block/bio.c
fs/iomap.c
fs/xfs/xfs_aops.c
include/linux/bio.h

index ce797d73bb436afafdc0434d708426813836ea95..67bba12d273bcbb5da754bea712becd2801dbe55 100644 (file)
@@ -731,7 +731,7 @@ static int __bio_add_pc_page(struct request_queue *q, struct bio *bio,
                }
        }
 
-       if (bio_full(bio))
+       if (bio_full(bio, len))
                return 0;
 
        if (bio->bi_phys_segments >= queue_max_segments(q))
@@ -807,7 +807,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;
@@ -834,7 +834,7 @@ int bio_add_page(struct bio *bio, struct page *page,
        bool same_page = false;
 
        if (!__bio_try_merge_page(bio, page, len, offset, &same_page)) {
-               if (bio_full(bio))
+               if (bio_full(bio, len))
                        return 0;
                __bio_add_page(bio, page, len, offset);
        }
@@ -922,7 +922,7 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
                        if (same_page)
                                put_page(page);
                } else {
-                       if (WARN_ON_ONCE(bio_full(bio)))
+                       if (WARN_ON_ONCE(bio_full(bio, len)))
                                 return -EINVAL;
                        __bio_add_page(bio, page, len, offset);
                }
@@ -966,7 +966,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
                        ret = __bio_iov_bvec_add_pages(bio, iter);
                else
                        ret = __bio_iov_iter_get_pages(bio, iter);
-       } while (!ret && iov_iter_count(iter) && !bio_full(bio));
+       } while (!ret && iov_iter_count(iter) && !bio_full(bio, 0));
 
        if (iov_iter_bvec_no_ref(iter))
                bio_set_flag(bio, BIO_NO_PAGE_REF);
index 12654c2e78f8ec4a7c14a03293b5ae19addfd9a2..da961fca3180521c9bbe566671e2257fb9a49ee2 100644 (file)
@@ -333,7 +333,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;
 
index 8da5e6637771c62f62920f6db413a2a81fc7af83..11f703d4a60568fff5c2fa3e93647ebc8424ca38 100644 (file)
@@ -782,7 +782,7 @@ xfs_add_to_ioend(
                atomic_inc(&iop->write_count);
 
        if (!merged) {
-               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);
        }
index f87abaa898f0035f21613dc66e12594d04201f0b..e36b8fc1b1c333fd207dc11586fce45f06e72bb6 100644 (file)
@@ -102,9 +102,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;
 }
 
 static inline bool bio_next_segment(const struct bio *bio,