diff options
author | Theodore Ts'o <tytso@mit.edu> | 2018-03-30 20:04:11 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2018-03-30 20:04:11 -0400 |
commit | 54dd0e0a1b255f115f8647fc6fb93273251b01b9 (patch) | |
tree | 36f9de7bca64d5ec2459c6d3d9255fea76ea40fa /fs | |
parent | 9496005d6ca4cf8f5ee8f828165a8956872dc59d (diff) |
ext4: add extra checks to ext4_xattr_block_get()
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
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/xattr.c | 26 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 11 |
2 files changed, 30 insertions, 7 deletions
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 6304e81bfe6a..499cb4b1fbd2 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -197,7 +197,7 @@ ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end, 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, int name_index, const char *name, 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, int name_index, const char *name, 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, int name_index, const char *name, 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, int name_index, const char *name, 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; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index dd54c4f995c8..f39cad2abe2a 100644 --- 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 */ |