ext4: allocate entire range in zero range (CVE-2015-0275)

plus earlier fixes for this function that it depends on

svn path=/dists/sid/linux/; revision=22473
This commit is contained in:
Ben Hutchings 2015-04-06 17:28:52 +00:00
parent a1a7477708
commit 999727f3c9
5 changed files with 184 additions and 0 deletions

3
debian/changelog vendored
View File

@ -181,6 +181,9 @@ linux (3.16.7-ckt9-1) UNRELEASED; urgency=medium
* IB/core: Prevent integer overflow in ib_umem_get address arithmetic
(CVE-2014-8159)
* Btrfs: make xattr replace operations atomic (CVE-2014-9710)
* ext4: fix ZERO_RANGE bug hidden by flag aliasing
* ext4: fix accidental flag aliasing in ext4_map_blocks flags
* ext4: allocate entire range in zero range (CVE-2015-0275)
-- Ian Campbell <ijc@debian.org> Wed, 18 Mar 2015 21:07:15 +0000

View File

@ -0,0 +1,72 @@
From: Lukas Czerner <lczerner@redhat.com>
Date: Fri, 3 Apr 2015 00:09:13 -0400
Subject: ext4: allocate entire range in zero range
Origin: https://git.kernel.org/cgit/linux/kernel/git/tytso/ext4.git/commit/?id=0f2af21aae11972fa924374ddcf52e88347cf5a8
Currently there is a bug in zero range code which causes zero range
calls to only allocate block aligned portion of the range, while
ignoring the rest in some cases.
In some cases, namely if the end of the range is past i_size, we do
attempt to preallocate the last nonaligned block. However this might
cause kernel to BUG() in some carefully designed zero range requests
on setups where page size > block size.
Fix this problem by first preallocating the entire range, including
the nonaligned edges and converting the written extents to unwritten
in the next step. This approach will also give us the advantage of
having the range to be as linearly contiguous as possible.
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4795,12 +4795,6 @@ static long ext4_zero_range(struct file
else
max_blocks -= lblk;
- flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
- EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
- EXT4_EX_NOCACHE;
- if (mode & FALLOC_FL_KEEP_SIZE)
- flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
-
mutex_lock(&inode->i_mutex);
/*
@@ -4817,15 +4811,28 @@ static long ext4_zero_range(struct file
ret = inode_newsize_ok(inode, new_size);
if (ret)
goto out_mutex;
- /*
- * If we have a partial block after EOF we have to allocate
- * the entire block.
- */
- if (partial_end)
- max_blocks += 1;
}
+ flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
+ if (mode & FALLOC_FL_KEEP_SIZE)
+ flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
+
+ /* Preallocate the range including the unaligned edges */
+ if (partial_begin || partial_end) {
+ ret = ext4_alloc_file_blocks(file,
+ round_down(offset, 1 << blkbits) >> blkbits,
+ (round_up((offset + len), 1 << blkbits) -
+ round_down(offset, 1 << blkbits)) >> blkbits,
+ new_size, flags, mode);
+ if (ret)
+ goto out_mutex;
+
+ }
+
+ /* Zero range excluding the unaligned edges */
if (max_blocks > 0) {
+ flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
+ EXT4_EX_NOCACHE);
/* Now release the pages and zero block aligned part of pages*/
truncate_pagecache_range(inode, start, end - 1);

View File

@ -0,0 +1,40 @@
From: Theodore Ts'o <tytso@mit.edu>
Date: Mon, 1 Sep 2014 14:33:09 -0400
Subject: [2/2] ext4: fix accidental flag aliasing in ext4_map_blocks flags
Origin: https://git.kernel.org/linus/bd30d702fc320085f178d22866b32fdc4736c991
Commit b8a8684502a0f introduced an accidental flag aliasing between
EXT4_EX_NOCACHE and EXT4_GET_BLOCKS_CONVERT_UNWRITTEN.
Fortunately, this didn't introduce any untorward side effects --- we
got lucky. Nevertheless, fix this and leave a warning to hopefully
avoid this from happening in the future.
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
fs/ext4/ext4.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index cf3ad75..550b4f9 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -569,6 +569,7 @@ enum {
#define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200
/* Convert written extents to unwritten */
#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0400
+/* DO NOT ASSIGN ADDITIONAL FLAG VALUES WITHOUT ADJUSTING THE FLAGS BELOW */
/*
* The bit position of these flags must not overlap with any of the
@@ -579,8 +580,8 @@ enum {
* caching the extents when reading from the extent tree while a
* truncate or punch hole operation is in progress.
*/
-#define EXT4_EX_NOCACHE 0x0400
-#define EXT4_EX_FORCE_CACHE 0x0800
+#define EXT4_EX_NOCACHE 0x0800
+#define EXT4_EX_FORCE_CACHE 0x1000
/*
* Flags used by ext4_free_blocks

View File

@ -0,0 +1,66 @@
From: Theodore Ts'o <tytso@mit.edu>
Date: Mon, 1 Sep 2014 14:32:09 -0400
Subject: [1/2] ext4: fix ZERO_RANGE bug hidden by flag aliasing
Origin: https://git.kernel.org/linus/713e8dde3e71e92db2d8cc8459d236ce1fb576ce
We accidently aliased EXT4_EX_NOCACHE and EXT4_GET_CONVERT_UNWRITTEN
falgs, which apparently was hiding a bug that was unmasked when this
flag aliasing issue was addressed (see the subsequent commit). The
reproduction case was:
fsx -N 10000 -l 500000 -r 4096 -t 4096 -w 4096 -Z -R -W /vdb/junk
... which would cause fsx to report corruption in the data file.
The fix we have is a bit of an overkill, but I'd much rather be
conservative for now, and we can optimize ZERO_RANGE_FL handling
later. The fact that we need to zap the extent_status cache for the
inode is unfortunate, but correctness is far more important than
performance.
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: Namjae Jeon <namjae.jeon@samsung.com>
---
fs/ext4/extents.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4796,7 +4796,8 @@ static long ext4_zero_range(struct file
max_blocks -= lblk;
flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
- EXT4_GET_BLOCKS_CONVERT_UNWRITTEN;
+ EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
+ EXT4_EX_NOCACHE;
if (mode & FALLOC_FL_KEEP_SIZE)
flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
@@ -4834,15 +4835,21 @@ static long ext4_zero_range(struct file
ext4_inode_block_unlocked_dio(inode);
inode_dio_wait(inode);
+ ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
+ flags, mode);
+ if (ret)
+ goto out_dio;
/*
* Remove entire range from the extent status tree.
+ *
+ * ext4_es_remove_extent(inode, lblk, max_blocks) is
+ * NOT sufficient. I'm not sure why this is the case,
+ * but let's be conservative and remove the extent
+ * status tree for the entire inode. There should be
+ * no outstanding delalloc extents thanks to the
+ * filemap_write_and_wait_range() call above.
*/
- ret = ext4_es_remove_extent(inode, lblk, max_blocks);
- if (ret)
- goto out_dio;
-
- ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
- flags, mode);
+ ret = ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
if (ret)
goto out_dio;
}

View File

@ -557,3 +557,6 @@ debian/usb-avoid-abi-change-in-3.16.7-ckt8.patch
bugfix/all/ib-core-prevent-integer-overflow-in-ib_umem_get.patch
bugfix/all/btrfs-make-xattr-replace-operations-atomic.patch
bugfix/all/ext4-fix-zero_range-bug-hidden-by-flag-aliasing.patch
bugfix/all/ext4-fix-accidental-flag-aliasing-in-ext4_map_blocks.patch
bugfix/all/ext4-allocate-entire-range-in-zero-range.patch