| From 54dd0e0a1b255f115f8647fc6fb93273251b01b9 Mon Sep 17 00:00:00 2001 |
| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Fri, 30 Mar 2018 20:04:11 -0400 |
| Subject: ext4: add extra checks to ext4_xattr_block_get() |
| |
| From: Theodore Ts'o <tytso@mit.edu> |
| |
| commit 54dd0e0a1b255f115f8647fc6fb93273251b01b9 upstream. |
| |
| Add explicit checks in ext4_xattr_block_get() just in case the |
| e_value_offs and e_value_size fields in the the xattr block are |
| corrupted in memory after the buffer_verified bit is set on the xattr |
| block. |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ext4/xattr.c | 26 +++++++++++++++++++------- |
| fs/ext4/xattr.h | 11 +++++++++++ |
| 2 files changed, 30 insertions(+), 7 deletions(-) |
| |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -197,7 +197,7 @@ ext4_xattr_check_entries(struct ext4_xat |
| while (!IS_LAST_ENTRY(entry)) { |
| u32 size = le32_to_cpu(entry->e_value_size); |
| |
| - if (size > INT_MAX) |
| + if (size > EXT4_XATTR_SIZE_MAX) |
| return -EFSCORRUPTED; |
| |
| if (size != 0 && entry->e_value_inum == 0) { |
| @@ -540,8 +540,10 @@ ext4_xattr_block_get(struct inode *inode |
| if (error) |
| goto cleanup; |
| size = le32_to_cpu(entry->e_value_size); |
| + error = -ERANGE; |
| + if (unlikely(size > EXT4_XATTR_SIZE_MAX)) |
| + goto cleanup; |
| if (buffer) { |
| - error = -ERANGE; |
| if (size > buffer_size) |
| goto cleanup; |
| if (entry->e_value_inum) { |
| @@ -550,8 +552,12 @@ ext4_xattr_block_get(struct inode *inode |
| if (error) |
| goto cleanup; |
| } else { |
| - memcpy(buffer, bh->b_data + |
| - le16_to_cpu(entry->e_value_offs), size); |
| + u16 offset = le16_to_cpu(entry->e_value_offs); |
| + void *p = bh->b_data + offset; |
| + |
| + if (unlikely(p + size > end)) |
| + goto cleanup; |
| + memcpy(buffer, p, size); |
| } |
| } |
| error = size; |
| @@ -589,8 +595,10 @@ ext4_xattr_ibody_get(struct inode *inode |
| if (error) |
| goto cleanup; |
| size = le32_to_cpu(entry->e_value_size); |
| + error = -ERANGE; |
| + if (unlikely(size > EXT4_XATTR_SIZE_MAX)) |
| + goto cleanup; |
| if (buffer) { |
| - error = -ERANGE; |
| if (size > buffer_size) |
| goto cleanup; |
| if (entry->e_value_inum) { |
| @@ -599,8 +607,12 @@ ext4_xattr_ibody_get(struct inode *inode |
| if (error) |
| goto cleanup; |
| } else { |
| - memcpy(buffer, (void *)IFIRST(header) + |
| - le16_to_cpu(entry->e_value_offs), size); |
| + u16 offset = le16_to_cpu(entry->e_value_offs); |
| + void *p = (void *)IFIRST(header) + offset; |
| + |
| + if (unlikely(p + size > end)) |
| + goto cleanup; |
| + memcpy(buffer, p, size); |
| } |
| } |
| error = size; |
| --- a/fs/ext4/xattr.h |
| +++ b/fs/ext4/xattr.h |
| @@ -71,6 +71,17 @@ struct ext4_xattr_entry { |
| #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) |
| |
| /* |
| + * XATTR_SIZE_MAX is currently 64k, but for the purposes of checking |
| + * for file system consistency errors, we use a somewhat bigger value. |
| + * This allows XATTR_SIZE_MAX to grow in the future, but by using this |
| + * instead of INT_MAX for certain consistency checks, we don't need to |
| + * worry about arithmetic overflows. (Actually XATTR_SIZE_MAX is |
| + * defined in include/uapi/linux/limits.h, so changing it is going |
| + * not going to be trivial....) |
| + */ |
| +#define EXT4_XATTR_SIZE_MAX (1 << 24) |
| + |
| +/* |
| * The minimum size of EA value when you start storing it in an external inode |
| * size of block - size of header - size of 1 entry - 4 null bytes |
| */ |