1 ext4: check EA value offset when loading
3 From: "Darrick J. Wong" <darrick.wong@oracle.com>
5 When loading extended attributes, check each entry's value offset to
6 make sure it doesn't collide with the entries.
8 Without this check it is easy to crash the kernel by mounting a
9 malicious FS containing a file with an EA wherein e_value_offs = 0 and
10 e_value_size > 0 and then deleting the EA, which corrupts the name
13 (See the f_ea_value_crash test's FS image in e2fsprogs for an example.)
15 Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
16 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
17 Cc: stable@vger.kernel.org
19 fs/ext4/xattr.c | 32 ++++++++++++++++++++++++--------
20 1 file changed, 24 insertions(+), 8 deletions(-)
22 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
23 index da4df70..42823ab 100644
26 @@ -190,14 +190,28 @@ ext4_listxattr(struct dentry *dentry, char *buffer, size_t size)
30 -ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end)
31 +ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end,
34 - while (!IS_LAST_ENTRY(entry)) {
35 - struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);
36 + struct ext4_xattr_entry *e = entry;
38 + while (!IS_LAST_ENTRY(e)) {
39 + struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
40 if ((void *)next >= end)
46 + while (!IS_LAST_ENTRY(entry)) {
47 + if (entry->e_value_size != 0 &&
48 + (value_start + le16_to_cpu(entry->e_value_offs) <
49 + (void *)e + sizeof(__u32) ||
50 + value_start + le16_to_cpu(entry->e_value_offs) +
51 + le32_to_cpu(entry->e_value_size) > end))
53 + entry = EXT4_XATTR_NEXT(entry);
59 @@ -214,7 +228,8 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
61 if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
63 - error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
64 + error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size,
67 set_buffer_verified(bh);
69 @@ -331,7 +346,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
70 header = IHDR(inode, raw_inode);
71 entry = IFIRST(header);
72 end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
73 - error = ext4_xattr_check_names(entry, end);
74 + error = ext4_xattr_check_names(entry, end, entry);
77 error = ext4_xattr_find_entry(&entry, name_index, name,
78 @@ -463,7 +478,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
79 raw_inode = ext4_raw_inode(&iloc);
80 header = IHDR(inode, raw_inode);
81 end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
82 - error = ext4_xattr_check_names(IFIRST(header), end);
83 + error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header));
86 error = ext4_xattr_list_entries(dentry, IFIRST(header),
87 @@ -980,7 +995,8 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
88 is->s.here = is->s.first;
89 is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
90 if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
91 - error = ext4_xattr_check_names(IFIRST(header), is->s.end);
92 + error = ext4_xattr_check_names(IFIRST(header), is->s.end,
96 /* Find the named attribute. */