diff --git a/debian/changelog b/debian/changelog index d64da0a64..55731ec23 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Wed, 18 Mar 2015 21:07:15 +0000 diff --git a/debian/patches/bugfix/all/ext4-allocate-entire-range-in-zero-range.patch b/debian/patches/bugfix/all/ext4-allocate-entire-range-in-zero-range.patch new file mode 100644 index 000000000..99bcc3248 --- /dev/null +++ b/debian/patches/bugfix/all/ext4-allocate-entire-range-in-zero-range.patch @@ -0,0 +1,72 @@ +From: Lukas Czerner +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 +Signed-off-by: Theodore Ts'o +--- +--- 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); diff --git a/debian/patches/bugfix/all/ext4-fix-accidental-flag-aliasing-in-ext4_map_blocks.patch b/debian/patches/bugfix/all/ext4-fix-accidental-flag-aliasing-in-ext4_map_blocks.patch new file mode 100644 index 000000000..3cb22b984 --- /dev/null +++ b/debian/patches/bugfix/all/ext4-fix-accidental-flag-aliasing-in-ext4_map_blocks.patch @@ -0,0 +1,40 @@ +From: Theodore Ts'o +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 +--- + 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 diff --git a/debian/patches/bugfix/all/ext4-fix-zero_range-bug-hidden-by-flag-aliasing.patch b/debian/patches/bugfix/all/ext4-fix-zero_range-bug-hidden-by-flag-aliasing.patch new file mode 100644 index 000000000..15453504d --- /dev/null +++ b/debian/patches/bugfix/all/ext4-fix-zero_range-bug-hidden-by-flag-aliasing.patch @@ -0,0 +1,66 @@ +From: Theodore Ts'o +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 +Cc: Namjae Jeon +--- + 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; + } diff --git a/debian/patches/series b/debian/patches/series index 4d7dfa42c..08fc9920b 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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