diff options
Diffstat (limited to 'recipes-kernel/linux')
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon/Btrfs-CVE-2015-8374.patch | 293 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon_3.19.bb | 1 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon_4.1.bb | 1 |
3 files changed, 295 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 @@ | |||
1 | From f1008f6d21ec52d533f7473e2e46218408fb4580 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filipe Manana <fdmanana@suse.com> | ||
3 | Date: Fri, 16 Oct 2015 12:34:25 +0100 | ||
4 | Subject: Btrfs: fix truncation of compressed and inlined extents | ||
5 | |||
6 | commit 0305cd5f7fca85dae392b9ba85b116896eb7c1c7 upstream. | ||
7 | |||
8 | When truncating a file to a smaller size which consists of an inline | ||
9 | extent that is compressed, we did not discard (or made unusable) the | ||
10 | data between the new file size and the old file size, wasting metadata | ||
11 | space and allowing for the truncated data to be leaked and the data | ||
12 | corruption/loss mentioned below. | ||
13 | We were also not correctly decrementing the number of bytes used by the | ||
14 | inode, we were setting it to zero, giving a wrong report for callers of | ||
15 | the stat(2) syscall. The fsck tool also reported an error about a mismatch | ||
16 | between the nbytes of the file versus the real space used by the file. | ||
17 | |||
18 | Now because we weren't discarding the truncated region of the file, it | ||
19 | was possible for a caller of the clone ioctl to actually read the data | ||
20 | that was truncated, allowing for a security breach without requiring root | ||
21 | access to the system, using only standard filesystem operations. The | ||
22 | scenario 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 | |||
44 | Note that this contrasts with the case where we truncate a file from 2000 | ||
45 | bytes to 1000 bytes and then truncate it back from 1000 to 2000 bytes. In | ||
46 | this case reading any byte from the range [1000, 2000[ will return a value | ||
47 | of 0x00, instead of the original data. | ||
48 | |||
49 | This problem exists since the clone ioctl was added and happens both with | ||
50 | and without my recent data loss and file corruption fixes for the clone | ||
51 | ioctl (patch "Btrfs: fix file corruption and data loss after cloning | ||
52 | inline extents"). | ||
53 | |||
54 | So fix this by truncating the compressed inline extents as we do for the | ||
55 | non-compressed case, which involves decompressing, if the data isn't already | ||
56 | in the page cache, compressing the truncated version of the extent, writing | ||
57 | the compressed content into the inline extent and then truncate it. | ||
58 | |||
59 | The following test case for fstests reproduces the problem. In order for | ||
60 | the test to pass both this fix and my previous fix for the clone ioctl | ||
61 | that forbids cloning a smaller inline extent into a larger one, | ||
62 | which is titled "Btrfs: fix file corruption and data loss after cloning | ||
63 | inline extents", are needed. Without that other fix the test fails in a | ||
64 | different way that does not leak the truncated data, instead part of | ||
65 | destination file gets replaced with zeroes (because the destination file | ||
66 | has 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 | ||
174 | CVE: CVE-2015-8374 | ||
175 | Upstream-Status: Backport | ||
176 | |||
177 | Signed-off-by: Filipe Manana <fdmanana@suse.com> | ||
178 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
179 | Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> | ||
180 | --- | ||
181 | fs/btrfs/inode.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++---------- | ||
182 | 1 file changed, 68 insertions(+), 14 deletions(-) | ||
183 | |||
184 | diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c | ||
185 | index 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 | -- | ||
292 | cgit v0.12 | ||
293 | |||
diff --git a/recipes-kernel/linux/linux-hierofalcon_3.19.bb b/recipes-kernel/linux/linux-hierofalcon_3.19.bb index d56f1ff..55ccec7 100644 --- a/recipes-kernel/linux/linux-hierofalcon_3.19.bb +++ b/recipes-kernel/linux/linux-hierofalcon_3.19.bb | |||
@@ -35,6 +35,7 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-3.19;branch="standard/qemuarm6 | |||
35 | file://ipc-CVE-2015-7613.patch \ | 35 | file://ipc-CVE-2015-7613.patch \ |
36 | file://net-unix-CVE-2013-7446.patch \ | 36 | file://net-unix-CVE-2013-7446.patch \ |
37 | file://ALSA-CVE-2016-2546.patch \ | 37 | file://ALSA-CVE-2016-2546.patch \ |
38 | file://Btrfs-CVE-2015-8374.patch \ | ||
38 | " | 39 | " |
39 | 40 | ||
40 | S = "${WORKDIR}/git" | 41 | S = "${WORKDIR}/git" |
diff --git a/recipes-kernel/linux/linux-hierofalcon_4.1.bb b/recipes-kernel/linux/linux-hierofalcon_4.1.bb index 2141668..f528b53 100644 --- a/recipes-kernel/linux/linux-hierofalcon_4.1.bb +++ b/recipes-kernel/linux/linux-hierofalcon_4.1.bb | |||
@@ -36,6 +36,7 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-4.1;branch="standard/qemuarm64 | |||
36 | file://usb-CVE-2015-8816.patch \ | 36 | file://usb-CVE-2015-8816.patch \ |
37 | file://bpf-CVE-2016-2383.patch \ | 37 | file://bpf-CVE-2016-2383.patch \ |
38 | file://ALSA-CVE-2016-2546.patch \ | 38 | file://ALSA-CVE-2016-2546.patch \ |
39 | file://Btrfs-CVE-2015-8374.patch \ | ||
39 | " | 40 | " |
40 | 41 | ||
41 | S = "${WORKDIR}/git" | 42 | S = "${WORKDIR}/git" |