| From 5369a762c882c0b6e9599e4ebbb3a9ba9eee7e2d Mon Sep 17 00:00:00 2001 |
| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Wed, 13 Jun 2018 00:23:11 -0400 |
| Subject: ext4: add corruption check in ext4_xattr_set_entry() |
| |
| From: Theodore Ts'o <tytso@mit.edu> |
| |
| commit 5369a762c882c0b6e9599e4ebbb3a9ba9eee7e2d upstream. |
| |
| In theory this should have been caught earlier when the xattr list was |
| verified, but in case it got missed, it's simple enough to add check |
| to make sure we don't overrun the xattr buffer. |
| |
| This addresses CVE-2018-10879. |
| |
| https://bugzilla.kernel.org/show_bug.cgi?id=200001 |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Reviewed-by: Andreas Dilger <adilger@dilger.ca> |
| [bwh: Backported to 3.16: |
| - Add inode parameter to ext4_xattr_set_entry() and update callers |
| - Return -EIO instead of -EFSCORRUPTED on error |
| - Adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| [adjusted context for 4.9] |
| Signed-off-by: Daniel Rosenberg <drosen@google.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/xattr.c | 22 ++++++++++++++-------- |
| 1 file changed, 14 insertions(+), 8 deletions(-) |
| |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -645,14 +645,20 @@ static size_t ext4_xattr_free_space(stru |
| } |
| |
| static int |
| -ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s) |
| +ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s, |
| + struct inode *inode) |
| { |
| - struct ext4_xattr_entry *last; |
| + struct ext4_xattr_entry *last, *next; |
| size_t free, min_offs = s->end - s->base, name_len = strlen(i->name); |
| |
| /* Compute min_offs and last. */ |
| last = s->first; |
| - for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { |
| + for (; !IS_LAST_ENTRY(last); last = next) { |
| + next = EXT4_XATTR_NEXT(last); |
| + if ((void *)next >= s->end) { |
| + EXT4_ERROR_INODE(inode, "corrupted xattr entries"); |
| + return -EIO; |
| + } |
| if (last->e_value_size) { |
| size_t offs = le16_to_cpu(last->e_value_offs); |
| if (offs < min_offs) |
| @@ -834,7 +840,7 @@ ext4_xattr_block_set(handle_t *handle, s |
| mb_cache_entry_delete_block(ext4_mb_cache, hash, |
| bs->bh->b_blocknr); |
| ea_bdebug(bs->bh, "modifying in-place"); |
| - error = ext4_xattr_set_entry(i, s); |
| + error = ext4_xattr_set_entry(i, s, inode); |
| if (!error) { |
| if (!IS_LAST_ENTRY(s->first)) |
| ext4_xattr_rehash(header(s->base), |
| @@ -881,7 +887,7 @@ ext4_xattr_block_set(handle_t *handle, s |
| s->end = s->base + sb->s_blocksize; |
| } |
| |
| - error = ext4_xattr_set_entry(i, s); |
| + error = ext4_xattr_set_entry(i, s, inode); |
| if (error == -EFSCORRUPTED) |
| goto bad_block; |
| if (error) |
| @@ -1079,7 +1085,7 @@ int ext4_xattr_ibody_inline_set(handle_t |
| |
| if (EXT4_I(inode)->i_extra_isize == 0) |
| return -ENOSPC; |
| - error = ext4_xattr_set_entry(i, s); |
| + error = ext4_xattr_set_entry(i, s, inode); |
| if (error) { |
| if (error == -ENOSPC && |
| ext4_has_inline_data(inode)) { |
| @@ -1091,7 +1097,7 @@ int ext4_xattr_ibody_inline_set(handle_t |
| error = ext4_xattr_ibody_find(inode, i, is); |
| if (error) |
| return error; |
| - error = ext4_xattr_set_entry(i, s); |
| + error = ext4_xattr_set_entry(i, s, inode); |
| } |
| if (error) |
| return error; |
| @@ -1117,7 +1123,7 @@ static int ext4_xattr_ibody_set(handle_t |
| |
| if (EXT4_I(inode)->i_extra_isize == 0) |
| return -ENOSPC; |
| - error = ext4_xattr_set_entry(i, s); |
| + error = ext4_xattr_set_entry(i, s, inode); |
| if (error) |
| return error; |
| header = IHDR(inode, ext4_raw_inode(&is->iloc)); |