block: add bio_truncate to fix guard_bio_eod

Some filesystem, such as vfat, may send bio which crosses device boundary,
and the worse thing is that the IO request starting within device boundaries
can contain more than one segment past EOD.

Commit dce30ca9e3b6 ("fs: fix guard_bio_eod to check for real EOD errors")
tries to fix this issue by returning -EIO for this situation. However,
this way lets fs user code lose chance to handle -EIO, then sync_inodes_sb()
may hang for ever.

Also the current truncating on last segment is dangerous by updating the
last bvec, given bvec table becomes not immutable any more, and fs bio
users may not retrieve the truncated pages via bio_for_each_segment_all() in
its .end_io callback.

Fixes this issue by supporting multi-segment truncating. And the
approach is simpler:

- just update bio size since block layer can make correct bvec with
the updated bio size. Then bvec table becomes really immutable.

- zero all truncated segments for read bio

Cc: Carlos Maiolino <cmaiolino@redhat.com>
Cc: linux-fsdevel@vger.kernel.org
Fixed-by: dce30ca9e3b6 ("fs: fix guard_bio_eod to check for real EOD errors")
Reported-by: syzbot+2b9e54155c8c25d8d165@syzkaller.appspotmail.com
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
85a8ce62 b2c0fcd2

+41 -24
+39
block/bio.c
··· 538 } 539 EXPORT_SYMBOL(zero_fill_bio_iter); 540 541 /** 542 * bio_put - release a reference to a bio 543 * @bio: bio to release reference to
··· 538 } 539 EXPORT_SYMBOL(zero_fill_bio_iter); 540 541 + void bio_truncate(struct bio *bio, unsigned new_size) 542 + { 543 + struct bio_vec bv; 544 + struct bvec_iter iter; 545 + unsigned int done = 0; 546 + bool truncated = false; 547 + 548 + if (new_size >= bio->bi_iter.bi_size) 549 + return; 550 + 551 + if (bio_data_dir(bio) != READ) 552 + goto exit; 553 + 554 + bio_for_each_segment(bv, bio, iter) { 555 + if (done + bv.bv_len > new_size) { 556 + unsigned offset; 557 + 558 + if (!truncated) 559 + offset = new_size - done; 560 + else 561 + offset = 0; 562 + zero_user(bv.bv_page, offset, bv.bv_len - offset); 563 + truncated = true; 564 + } 565 + done += bv.bv_len; 566 + } 567 + 568 + exit: 569 + /* 570 + * Don't touch bvec table here and make it really immutable, since 571 + * fs bio user has to retrieve all pages via bio_for_each_segment_all 572 + * in its .end_bio() callback. 573 + * 574 + * It is enough to truncate bio by updating .bi_size since we can make 575 + * correct bvec with the updated .bi_size for drivers. 576 + */ 577 + bio->bi_iter.bi_size = new_size; 578 + } 579 + 580 /** 581 * bio_put - release a reference to a bio 582 * @bio: bio to release reference to
+1 -24
fs/buffer.c
··· 3034 void guard_bio_eod(int op, struct bio *bio) 3035 { 3036 sector_t maxsector; 3037 - struct bio_vec *bvec = bio_last_bvec_all(bio); 3038 - unsigned truncated_bytes; 3039 struct hd_struct *part; 3040 3041 rcu_read_lock(); ··· 3059 if (likely((bio->bi_iter.bi_size >> 9) <= maxsector)) 3060 return; 3061 3062 - /* Uhhuh. We've got a bio that straddles the device size! */ 3063 - truncated_bytes = bio->bi_iter.bi_size - (maxsector << 9); 3064 - 3065 - /* 3066 - * The bio contains more than one segment which spans EOD, just return 3067 - * and let IO layer turn it into an EIO 3068 - */ 3069 - if (truncated_bytes > bvec->bv_len) 3070 - return; 3071 - 3072 - /* Truncate the bio.. */ 3073 - bio->bi_iter.bi_size -= truncated_bytes; 3074 - bvec->bv_len -= truncated_bytes; 3075 - 3076 - /* ..and clear the end of the buffer for reads */ 3077 - if (op == REQ_OP_READ) { 3078 - struct bio_vec bv; 3079 - 3080 - mp_bvec_last_segment(bvec, &bv); 3081 - zero_user(bv.bv_page, bv.bv_offset + bv.bv_len, 3082 - truncated_bytes); 3083 - } 3084 } 3085 3086 static int submit_bh_wbc(int op, int op_flags, struct buffer_head *bh,
··· 3034 void guard_bio_eod(int op, struct bio *bio) 3035 { 3036 sector_t maxsector; 3037 struct hd_struct *part; 3038 3039 rcu_read_lock(); ··· 3061 if (likely((bio->bi_iter.bi_size >> 9) <= maxsector)) 3062 return; 3063 3064 + bio_truncate(bio, maxsector << 9); 3065 } 3066 3067 static int submit_bh_wbc(int op, int op_flags, struct buffer_head *bh,
+1
include/linux/bio.h
··· 470 gfp_t); 471 extern int bio_uncopy_user(struct bio *); 472 void zero_fill_bio_iter(struct bio *bio, struct bvec_iter iter); 473 474 static inline void zero_fill_bio(struct bio *bio) 475 {
··· 470 gfp_t); 471 extern int bio_uncopy_user(struct bio *); 472 void zero_fill_bio_iter(struct bio *bio, struct bvec_iter iter); 473 + void bio_truncate(struct bio *bio, unsigned new_size); 474 475 static inline void zero_fill_bio(struct bio *bio) 476 {