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