summaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch
diff options
context:
space:
mode:
authorSona Sarmadi <sona.sarmadi@enea.com>2016-04-06 08:53:36 +0200
committerTudor Florea <tudor.florea@enea.com>2016-04-07 03:12:56 +0200
commit291c45aaebb29078e32ff38f7b9998fd9fdfe167 (patch)
tree48f9f2a87949def8757f1b9868209c336febb3fc /recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch
parent95e2d54d188fa653d9075782a3e431925829c297 (diff)
downloadmeta-hierofalcon-291c45aaebb29078e32ff38f7b9998fd9fdfe167.tar.gz
kernel/Btrfs: CVE-2015-8374
Fixes an information-leak vulnerability in the kernel when it truncated a file to a smaller size which consisted of an inline extent that was compressed. A caller of the clone ioctl could exploit this flaw by using only standard file-system operations without root access to read the truncated data. Reference: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-8374 Reference to upstream patch: https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/ patch/?id=f1008f6d21ec52d533f7473e2e46218408fb4580 Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> Signed-off-by: Tudor Florea <tudor.florea@enea.com>
Diffstat (limited to 'recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch')
-rw-r--r--recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch293
1 files changed, 293 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch b/recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch
new file mode 100644
index 0000000..8ea1ac6
--- /dev/null
+++ b/recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch
@@ -0,0 +1,293 @@
1From f1008f6d21ec52d533f7473e2e46218408fb4580 Mon Sep 17 00:00:00 2001
2From: Filipe Manana <fdmanana@suse.com>
3Date: Fri, 16 Oct 2015 12:34:25 +0100
4Subject: Btrfs: fix truncation of compressed and inlined extents
5
6commit 0305cd5f7fca85dae392b9ba85b116896eb7c1c7 upstream.
7
8When truncating a file to a smaller size which consists of an inline
9extent that is compressed, we did not discard (or made unusable) the
10data between the new file size and the old file size, wasting metadata
11space and allowing for the truncated data to be leaked and the data
12corruption/loss mentioned below.
13We were also not correctly decrementing the number of bytes used by the
14inode, we were setting it to zero, giving a wrong report for callers of
15the stat(2) syscall. The fsck tool also reported an error about a mismatch
16between the nbytes of the file versus the real space used by the file.
17
18Now because we weren't discarding the truncated region of the file, it
19was possible for a caller of the clone ioctl to actually read the data
20that was truncated, allowing for a security breach without requiring root
21access to the system, using only standard filesystem operations. The
22scenario is the following:
23
24 1) User A creates a file which consists of an inline and compressed
25 extent with a size of 2000 bytes - the file is not accessible to
26 any other users (no read, write or execution permission for anyone
27 else);
28
29 2) The user truncates the file to a size of 1000 bytes;
30
31 3) User A makes the file world readable;
32
33 4) User B creates a file consisting of an inline extent of 2000 bytes;
34
35 5) User B issues a clone operation from user A's file into its own
36 file (using a length argument of 0, clone the whole range);
37
38 6) User B now gets to see the 1000 bytes that user A truncated from
39 its file before it made its file world readbale. User B also lost
40 the bytes in the range [1000, 2000[ bytes from its own file, but
41 that might be ok if his/her intention was reading stale data from
42 user A that was never supposed to be public.
43
44Note that this contrasts with the case where we truncate a file from 2000
45bytes to 1000 bytes and then truncate it back from 1000 to 2000 bytes. In
46this case reading any byte from the range [1000, 2000[ will return a value
47of 0x00, instead of the original data.
48
49This problem exists since the clone ioctl was added and happens both with
50and without my recent data loss and file corruption fixes for the clone
51ioctl (patch "Btrfs: fix file corruption and data loss after cloning
52inline extents").
53
54So fix this by truncating the compressed inline extents as we do for the
55non-compressed case, which involves decompressing, if the data isn't already
56in the page cache, compressing the truncated version of the extent, writing
57the compressed content into the inline extent and then truncate it.
58
59The following test case for fstests reproduces the problem. In order for
60the test to pass both this fix and my previous fix for the clone ioctl
61that forbids cloning a smaller inline extent into a larger one,
62which is titled "Btrfs: fix file corruption and data loss after cloning
63inline extents", are needed. Without that other fix the test fails in a
64different way that does not leak the truncated data, instead part of
65destination file gets replaced with zeroes (because the destination file
66has a larger inline extent than the source).
67
68 seq=`basename $0`
69 seqres=$RESULT_DIR/$seq
70 echo "QA output created by $seq"
71 tmp=/tmp/$$
72 status=1 # failure is the default!
73 trap "_cleanup; exit \$status" 0 1 2 3 15
74
75 _cleanup()
76 {
77 rm -f $tmp.*
78 }
79
80 # get standard environment, filters and checks
81 . ./common/rc
82 . ./common/filter
83
84 # real QA test starts here
85 _need_to_be_root
86 _supported_fs btrfs
87 _supported_os Linux
88 _require_scratch
89 _require_cloner
90
91 rm -f $seqres.full
92
93 _scratch_mkfs >>$seqres.full 2>&1
94 _scratch_mount "-o compress"
95
96 # Create our test files. File foo is going to be the source of a clone operation
97 # and consists of a single inline extent with an uncompressed size of 512 bytes,
98 # while file bar consists of a single inline extent with an uncompressed size of
99 # 256 bytes. For our test's purpose, it's important that file bar has an inline
100 # extent with a size smaller than foo's inline extent.
101 $XFS_IO_PROG -f -c "pwrite -S 0xa1 0 128" \
102 -c "pwrite -S 0x2a 128 384" \
103 $SCRATCH_MNT/foo | _filter_xfs_io
104 $XFS_IO_PROG -f -c "pwrite -S 0xbb 0 256" $SCRATCH_MNT/bar | _filter_xfs_io
105
106 # Now durably persist all metadata and data. We do this to make sure that we get
107 # on disk an inline extent with a size of 512 bytes for file foo.
108 sync
109
110 # Now truncate our file foo to a smaller size. Because it consists of a
111 # compressed and inline extent, btrfs did not shrink the inline extent to the
112 # new size (if the extent was not compressed, btrfs would shrink it to 128
113 # bytes), it only updates the inode's i_size to 128 bytes.
114 $XFS_IO_PROG -c "truncate 128" $SCRATCH_MNT/foo
115
116 # Now clone foo's inline extent into bar.
117 # This clone operation should fail with errno EOPNOTSUPP because the source
118 # file consists only of an inline extent and the file's size is smaller than
119 # the inline extent of the destination (128 bytes < 256 bytes). However the
120 # clone ioctl was not prepared to deal with a file that has a size smaller
121 # than the size of its inline extent (something that happens only for compressed
122 # inline extents), resulting in copying the full inline extent from the source
123 # file into the destination file.
124 #
125 # Note that btrfs' clone operation for inline extents consists of removing the
126 # inline extent from the destination inode and copy the inline extent from the
127 # source inode into the destination inode, meaning that if the destination
128 # inode's inline extent is larger (N bytes) than the source inode's inline
129 # extent (M bytes), some bytes (N - M bytes) will be lost from the destination
130 # file. Btrfs could copy the source inline extent's data into the destination's
131 # inline extent so that we would not lose any data, but that's currently not
132 # done due to the complexity that would be needed to deal with such cases
133 # (specially when one or both extents are compressed), returning EOPNOTSUPP, as
134 # it's normally not a very common case to clone very small files (only case
135 # where we get inline extents) and copying inline extents does not save any
136 # space (unlike for normal, non-inlined extents).
137 $CLONER_PROG -s 0 -d 0 -l 0 $SCRATCH_MNT/foo $SCRATCH_MNT/bar
138
139 # Now because the above clone operation used to succeed, and due to foo's inline
140 # extent not being shinked by the truncate operation, our file bar got the whole
141 # inline extent copied from foo, making us lose the last 128 bytes from bar
142 # which got replaced by the bytes in range [128, 256[ from foo before foo was
143 # truncated - in other words, data loss from bar and being able to read old and
144 # stale data from foo that should not be possible to read anymore through normal
145 # filesystem operations. Contrast with the case where we truncate a file from a
146 # size N to a smaller size M, truncate it back to size N and then read the range
147 # [M, N[, we should always get the value 0x00 for all the bytes in that range.
148
149 # We expected the clone operation to fail with errno EOPNOTSUPP and therefore
150 # not modify our file's bar data/metadata. So its content should be 256 bytes
151 # long with all bytes having the value 0xbb.
152 #
153 # Without the btrfs bug fix, the clone operation succeeded and resulted in
154 # leaking truncated data from foo, the bytes that belonged to its range
155 # [128, 256[, and losing data from bar in that same range. So reading the
156 # file gave us the following content:
157 #
158 # 0000000 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1 a1
159 # *
160 # 0000200 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a
161 # *
162 # 0000400
163 echo "File bar's content after the clone operation:"
164 od -t x1 $SCRATCH_MNT/bar
165
166 # Also because the foo's inline extent was not shrunk by the truncate
167 # operation, btrfs' fsck, which is run by the fstests framework everytime a
168 # test completes, failed reporting the following error:
169 #
170 # root 5 inode 257 errors 400, nbytes wrong
171
172 status=0
173 exit
174CVE: CVE-2015-8374
175Upstream-Status: Backport
176
177Signed-off-by: Filipe Manana <fdmanana@suse.com>
178Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
179Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com>
180---
181 fs/btrfs/inode.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++----------
182 1 file changed, 68 insertions(+), 14 deletions(-)
183
184diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
185index e3b39f0..88680af 100644
186--- a/fs/btrfs/inode.c
187+++ b/fs/btrfs/inode.c
188@@ -4184,6 +4184,47 @@ static int truncate_space_check(struct btrfs_trans_handle *trans,
189
190 }
191
192+static int truncate_inline_extent(struct inode *inode,
193+ struct btrfs_path *path,
194+ struct btrfs_key *found_key,
195+ const u64 item_end,
196+ const u64 new_size)
197+{
198+ struct extent_buffer *leaf = path->nodes[0];
199+ int slot = path->slots[0];
200+ struct btrfs_file_extent_item *fi;
201+ u32 size = (u32)(new_size - found_key->offset);
202+ struct btrfs_root *root = BTRFS_I(inode)->root;
203+
204+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
205+
206+ if (btrfs_file_extent_compression(leaf, fi) != BTRFS_COMPRESS_NONE) {
207+ loff_t offset = new_size;
208+ loff_t page_end = ALIGN(offset, PAGE_CACHE_SIZE);
209+
210+ /*
211+ * Zero out the remaining of the last page of our inline extent,
212+ * instead of directly truncating our inline extent here - that
213+ * would be much more complex (decompressing all the data, then
214+ * compressing the truncated data, which might be bigger than
215+ * the size of the inline extent, resize the extent, etc).
216+ * We release the path because to get the page we might need to
217+ * read the extent item from disk (data not in the page cache).
218+ */
219+ btrfs_release_path(path);
220+ return btrfs_truncate_page(inode, offset, page_end - offset, 0);
221+ }
222+
223+ btrfs_set_file_extent_ram_bytes(leaf, fi, size);
224+ size = btrfs_file_extent_calc_inline_size(size);
225+ btrfs_truncate_item(root, path, size, 1);
226+
227+ if (test_bit(BTRFS_ROOT_REF_COWS, &root->state))
228+ inode_sub_bytes(inode, item_end + 1 - new_size);
229+
230+ return 0;
231+}
232+
233 /*
234 * this can truncate away extent items, csum items and directory items.
235 * It starts at a high offset and removes keys until it can't find
236@@ -4378,27 +4419,40 @@ search_again:
237 * special encodings
238 */
239 if (!del_item &&
240- btrfs_file_extent_compression(leaf, fi) == 0 &&
241 btrfs_file_extent_encryption(leaf, fi) == 0 &&
242 btrfs_file_extent_other_encoding(leaf, fi) == 0) {
243- u32 size = new_size - found_key.offset;
244-
245- if (test_bit(BTRFS_ROOT_REF_COWS, &root->state))
246- inode_sub_bytes(inode, item_end + 1 -
247- new_size);
248
249 /*
250- * update the ram bytes to properly reflect
251- * the new size of our item
252+ * Need to release path in order to truncate a
253+ * compressed extent. So delete any accumulated
254+ * extent items so far.
255 */
256- btrfs_set_file_extent_ram_bytes(leaf, fi, size);
257- size =
258- btrfs_file_extent_calc_inline_size(size);
259- btrfs_truncate_item(root, path, size, 1);
260+ if (btrfs_file_extent_compression(leaf, fi) !=
261+ BTRFS_COMPRESS_NONE && pending_del_nr) {
262+ err = btrfs_del_items(trans, root, path,
263+ pending_del_slot,
264+ pending_del_nr);
265+ if (err) {
266+ btrfs_abort_transaction(trans,
267+ root,
268+ err);
269+ goto error;
270+ }
271+ pending_del_nr = 0;
272+ }
273+
274+ err = truncate_inline_extent(inode, path,
275+ &found_key,
276+ item_end,
277+ new_size);
278+ if (err) {
279+ btrfs_abort_transaction(trans,
280+ root, err);
281+ goto error;
282+ }
283 } else if (test_bit(BTRFS_ROOT_REF_COWS,
284 &root->state)) {
285- inode_sub_bytes(inode, item_end + 1 -
286- found_key.offset);
287+ inode_sub_bytes(inode, item_end + 1 - new_size);
288 }
289 }
290 delete:
291--
292cgit v0.12
293