| From: Eric Biggers <ebiggers@google.com> |
| Date: Thu, 1 Dec 2016 14:51:58 -0500 |
| Subject: ext4: don't read out of bounds when checking for in-inode xattrs |
| |
| commit 290ab230016f187c3551d8380ea742889276d03a upstream. |
| |
| With i_extra_isize equal to or close to the available space, it was |
| possible for us to read past the end of the inode when trying to detect |
| or validate in-inode xattrs. Fix this by checking for the needed extra |
| space first. |
| |
| This patch shouldn't have any noticeable effect on |
| non-corrupted/non-malicious filesystems. |
| |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Reviewed-by: Andreas Dilger <adilger@dilger.ca> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/ext4/inode.c | 4 +++- |
| fs/ext4/xattr.c | 5 ++--- |
| 2 files changed, 5 insertions(+), 4 deletions(-) |
| |
| --- a/fs/ext4/inode.c |
| +++ b/fs/ext4/inode.c |
| @@ -4165,7 +4165,9 @@ static inline void ext4_iget_extra_inode |
| { |
| __le32 *magic = (void *)raw_inode + |
| EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize; |
| - if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) { |
| + if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize + sizeof(__le32) <= |
| + EXT4_INODE_SIZE(inode->i_sb) && |
| + *magic == cpu_to_le32(EXT4_XATTR_MAGIC)) { |
| ext4_set_inode_state(inode, EXT4_STATE_XATTR); |
| ext4_find_inline_data_nolock(inode); |
| } else |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -247,13 +247,12 @@ static int |
| __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, |
| void *end, const char *function, unsigned int line) |
| { |
| - struct ext4_xattr_entry *entry = IFIRST(header); |
| int error = -EIO; |
| |
| - if (((void *) header >= end) || |
| + if (end - (void *)header < sizeof(*header) + sizeof(u32) || |
| (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC))) |
| goto errout; |
| - error = ext4_xattr_check_names(entry, end, entry); |
| + error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); |
| errout: |
| if (error) |
| __ext4_error_inode(inode, function, line, 0, |