]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-4.4/fs-fix-guard_bio_eod-to-check-for-real-eod-errors.patch
Fixes for 4.19
[thirdparty/kernel/stable-queue.git] / queue-4.4 / fs-fix-guard_bio_eod-to-check-for-real-eod-errors.patch
1 From 6e1a7cd0a2e5a73c3e2d42eadd9cca00dcfbcff0 Mon Sep 17 00:00:00 2001
2 From: Carlos Maiolino <cmaiolino@redhat.com>
3 Date: Tue, 26 Feb 2019 11:51:50 +0100
4 Subject: fs: fix guard_bio_eod to check for real EOD errors
5
6 [ Upstream commit dce30ca9e3b676fb288c33c1f4725a0621361185 ]
7
8 guard_bio_eod() can truncate a segment in bio to allow it to do IO on
9 odd last sectors of a device.
10
11 It already checks if the IO starts past EOD, but it does not consider
12 the possibility of an IO request starting within device boundaries can
13 contain more than one segment past EOD.
14
15 In such cases, truncated_bytes can be bigger than PAGE_SIZE, and will
16 underflow bvec->bv_len.
17
18 Fix this by checking if truncated_bytes is lower than PAGE_SIZE.
19
20 This situation has been found on filesystems such as isofs and vfat,
21 which doesn't check the device size before mount, if the device is
22 smaller than the filesystem itself, a readahead on such filesystem,
23 which spans EOD, can trigger this situation, leading a call to
24 zero_user() with a wrong size possibly corrupting memory.
25
26 I didn't see any crash, or didn't let the system run long enough to
27 check if memory corruption will be hit somewhere, but adding
28 instrumentation to guard_bio_end() to check truncated_bytes size, was
29 enough to see the error.
30
31 The following script can trigger the error.
32
33 MNT=/mnt
34 IMG=./DISK.img
35 DEV=/dev/loop0
36
37 mkfs.vfat $IMG
38 mount $IMG $MNT
39 cp -R /etc $MNT &> /dev/null
40 umount $MNT
41
42 losetup -D
43
44 losetup --find --show --sizelimit 16247280 $IMG
45 mount $DEV $MNT
46
47 find $MNT -type f -exec cat {} + >/dev/null
48
49 Kudos to Eric Sandeen for coming up with the reproducer above
50
51 Reviewed-by: Ming Lei <ming.lei@redhat.com>
52 Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
53 Signed-off-by: Jens Axboe <axboe@kernel.dk>
54 Signed-off-by: Sasha Levin <sashal@kernel.org>
55 ---
56 fs/buffer.c | 7 +++++++
57 1 file changed, 7 insertions(+)
58
59 diff --git a/fs/buffer.c b/fs/buffer.c
60 index 6f7d519a093b..f278e27bd8c0 100644
61 --- a/fs/buffer.c
62 +++ b/fs/buffer.c
63 @@ -2985,6 +2985,13 @@ void guard_bio_eod(int rw, struct bio *bio)
64 /* Uhhuh. We've got a bio that straddles the device size! */
65 truncated_bytes = bio->bi_iter.bi_size - (maxsector << 9);
66
67 + /*
68 + * The bio contains more than one segment which spans EOD, just return
69 + * and let IO layer turn it into an EIO
70 + */
71 + if (truncated_bytes > bvec->bv_len)
72 + return;
73 +
74 /* Truncate the bio.. */
75 bio->bi_iter.bi_size -= truncated_bytes;
76 bvec->bv_len -= truncated_bytes;
77 --
78 2.19.1
79