Skip to content

Commit

Permalink
ext4: add extra checks to ext4_xattr_block_get()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: [email protected]
  • Loading branch information
tytso committed Mar 31, 2018
1 parent 9496005 commit 54dd0e0
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
26 changes: 19 additions & 7 deletions fs/ext4/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions fs/ext4/xattr.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ struct ext4_xattr_entry {
EXT4_I(inode)->i_extra_isize))
#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
Expand Down

0 comments on commit 54dd0e0

Please sign in to comment.