| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Tue, 22 Mar 2016 16:13:15 -0400 |
| Subject: ext4: check if in-inode xattr is corrupted in |
| ext4_expand_extra_isize_ea() |
| |
| commit 9e92f48c34eb2b9af9d12f892e2fe1fce5e8ce35 upstream. |
| |
| We aren't checking to see if the in-inode extended attribute is |
| corrupted before we try to expand the inode's extra isize fields. |
| |
| This can lead to potential crashes caused by the BUG_ON() check in |
| ext4_xattr_shift_entries(). |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| [bwh: Backported to 3.16: s/EFSCORRUPTED/EIO/] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/ext4/xattr.c | 32 ++++++++++++++++++++++++++++---- |
| 1 file changed, 28 insertions(+), 4 deletions(-) |
| |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -243,6 +243,27 @@ ext4_xattr_check_block(struct inode *ino |
| return error; |
| } |
| |
| +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) || |
| + (header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC))) |
| + goto errout; |
| + error = ext4_xattr_check_names(entry, end, entry); |
| +errout: |
| + if (error) |
| + __ext4_error_inode(inode, function, line, 0, |
| + "corrupted in-inode xattr"); |
| + return error; |
| +} |
| + |
| +#define xattr_check_inode(inode, header, end) \ |
| + __xattr_check_inode((inode), (header), (end), __func__, __LINE__) |
| + |
| static inline int |
| ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size) |
| { |
| @@ -368,7 +389,7 @@ ext4_xattr_ibody_get(struct inode *inode |
| header = IHDR(inode, raw_inode); |
| entry = IFIRST(header); |
| end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; |
| - error = ext4_xattr_check_names(entry, end, entry); |
| + error = xattr_check_inode(inode, header, end); |
| if (error) |
| goto cleanup; |
| error = xattr_find_entry(inode, &entry, end, name_index, name, |
| @@ -506,7 +527,7 @@ ext4_xattr_ibody_list(struct dentry *den |
| raw_inode = ext4_raw_inode(&iloc); |
| header = IHDR(inode, raw_inode); |
| end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; |
| - error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); |
| + error = xattr_check_inode(inode, header, end); |
| if (error) |
| goto cleanup; |
| error = ext4_xattr_list_entries(dentry, IFIRST(header), |
| @@ -1038,8 +1059,7 @@ int ext4_xattr_ibody_find(struct inode * |
| is->s.here = is->s.first; |
| is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; |
| if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { |
| - error = ext4_xattr_check_names(IFIRST(header), is->s.end, |
| - IFIRST(header)); |
| + error = xattr_check_inode(inode, header, is->s.end); |
| if (error) |
| return error; |
| /* Find the named attribute. */ |
| @@ -1319,6 +1339,10 @@ retry: |
| last = entry; |
| total_ino = sizeof(struct ext4_xattr_ibody_header); |
| |
| + error = xattr_check_inode(inode, header, end); |
| + if (error) |
| + goto cleanup; |
| + |
| free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); |
| if (free >= new_extra_isize) { |
| entry = IFIRST(header); |