diff options
| author | Eric Biggers <ebiggers@google.com> | 2018-04-12 11:48:09 -0400 |
|---|---|---|
| committer | Theodore Ts'o <tytso@mit.edu> | 2018-04-12 11:48:09 -0400 |
| commit | 349fa7d6e1935f49bf4161c4900711b2989180a9 (patch) | |
| tree | c8df82a97c0681357988b5d7611679896727242a | |
| parent | e40ff213898502d299351cc2fe1e350cd186f0d3 (diff) | |
ext4: prevent right-shifting extents beyond EXT_MAX_BLOCKS
During the "insert range" fallocate operation, extents starting at the
range offset are shifted "right" (to a higher file offset) by the range
length. But, as shown by syzbot, it's not validated that this doesn't
cause extents to be shifted beyond EXT_MAX_BLOCKS. In that case
->ee_block can wrap around, corrupting the extent tree.
Fix it by returning an error if the space between the end of the last
extent and EXT4_MAX_BLOCKS is smaller than the range being inserted.
This bug can be reproduced by running the following commands when the
current directory is on an ext4 filesystem with a 4k block size:
fallocate -l 8192 file
fallocate --keep-size -o 0xfffffffe000 -l 4096 -n file
fallocate --insert-range -l 8192 file
Then after unmounting the filesystem, e2fsck reports corruption.
Reported-by: syzbot+06c885be0edcdaeab40c@syzkaller.appspotmail.com
Fixes: 331573febb6a ("ext4: Add support FALLOC_FL_INSERT_RANGE for fallocate")
Cc: stable@vger.kernel.org # v4.2+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
| -rw-r--r-- | fs/ext4/extents.c | 16 |
1 files changed, 11 insertions, 5 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 0a7315961bac..c969275ce3ee 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
| @@ -5329,8 +5329,9 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, | |||
| 5329 | stop = le32_to_cpu(extent->ee_block); | 5329 | stop = le32_to_cpu(extent->ee_block); |
| 5330 | 5330 | ||
| 5331 | /* | 5331 | /* |
| 5332 | * In case of left shift, Don't start shifting extents until we make | 5332 | * For left shifts, make sure the hole on the left is big enough to |
| 5333 | * sure the hole is big enough to accommodate the shift. | 5333 | * accommodate the shift. For right shifts, make sure the last extent |
| 5334 | * won't be shifted beyond EXT_MAX_BLOCKS. | ||
| 5334 | */ | 5335 | */ |
| 5335 | if (SHIFT == SHIFT_LEFT) { | 5336 | if (SHIFT == SHIFT_LEFT) { |
| 5336 | path = ext4_find_extent(inode, start - 1, &path, | 5337 | path = ext4_find_extent(inode, start - 1, &path, |
| @@ -5350,9 +5351,14 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, | |||
| 5350 | 5351 | ||
| 5351 | if ((start == ex_start && shift > ex_start) || | 5352 | if ((start == ex_start && shift > ex_start) || |
| 5352 | (shift > start - ex_end)) { | 5353 | (shift > start - ex_end)) { |
| 5353 | ext4_ext_drop_refs(path); | 5354 | ret = -EINVAL; |
| 5354 | kfree(path); | 5355 | goto out; |
| 5355 | return -EINVAL; | 5356 | } |
| 5357 | } else { | ||
| 5358 | if (shift > EXT_MAX_BLOCKS - | ||
| 5359 | (stop + ext4_ext_get_actual_len(extent))) { | ||
| 5360 | ret = -EINVAL; | ||
| 5361 | goto out; | ||
| 5356 | } | 5362 | } |
| 5357 | } | 5363 | } |
| 5358 | 5364 | ||
