ext4: check EA value offset when loading
When loading extended attributes, check each entry's value offset to make sure it doesn't collide with the entries. Without this check it is easy to crash the kernel by mounting a malicious FS containing a file with an EA wherein e_value_offs = 0 and e_value_size > 0 and then deleting the EA, which corrupts the name list. (See the f_ea_value_crash test's FS image in e2fsprogs for an example.) Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@vger.kernel.org
This commit is contained in:
Родитель
684de57486
Коммит
a0626e7595
|
@ -190,14 +190,28 @@ ext4_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end)
|
ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end,
|
||||||
|
void *value_start)
|
||||||
{
|
{
|
||||||
while (!IS_LAST_ENTRY(entry)) {
|
struct ext4_xattr_entry *e = entry;
|
||||||
struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);
|
|
||||||
|
while (!IS_LAST_ENTRY(e)) {
|
||||||
|
struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
|
||||||
if ((void *)next >= end)
|
if ((void *)next >= end)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
entry = next;
|
e = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (!IS_LAST_ENTRY(entry)) {
|
||||||
|
if (entry->e_value_size != 0 &&
|
||||||
|
(value_start + le16_to_cpu(entry->e_value_offs) <
|
||||||
|
(void *)e + sizeof(__u32) ||
|
||||||
|
value_start + le16_to_cpu(entry->e_value_offs) +
|
||||||
|
le32_to_cpu(entry->e_value_size) > end))
|
||||||
|
return -EIO;
|
||||||
|
entry = EXT4_XATTR_NEXT(entry);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +228,8 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
|
if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
|
error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size,
|
||||||
|
bh->b_data);
|
||||||
if (!error)
|
if (!error)
|
||||||
set_buffer_verified(bh);
|
set_buffer_verified(bh);
|
||||||
return error;
|
return error;
|
||||||
|
@ -331,7 +346,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
|
||||||
header = IHDR(inode, raw_inode);
|
header = IHDR(inode, raw_inode);
|
||||||
entry = IFIRST(header);
|
entry = IFIRST(header);
|
||||||
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||||
error = ext4_xattr_check_names(entry, end);
|
error = ext4_xattr_check_names(entry, end, entry);
|
||||||
if (error)
|
if (error)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
error = ext4_xattr_find_entry(&entry, name_index, name,
|
error = ext4_xattr_find_entry(&entry, name_index, name,
|
||||||
|
@ -463,7 +478,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||||
raw_inode = ext4_raw_inode(&iloc);
|
raw_inode = ext4_raw_inode(&iloc);
|
||||||
header = IHDR(inode, raw_inode);
|
header = IHDR(inode, raw_inode);
|
||||||
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||||
error = ext4_xattr_check_names(IFIRST(header), end);
|
error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header));
|
||||||
if (error)
|
if (error)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
error = ext4_xattr_list_entries(dentry, IFIRST(header),
|
error = ext4_xattr_list_entries(dentry, IFIRST(header),
|
||||||
|
@ -980,7 +995,8 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
|
||||||
is->s.here = is->s.first;
|
is->s.here = is->s.first;
|
||||||
is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||||
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
|
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
|
||||||
error = ext4_xattr_check_names(IFIRST(header), is->s.end);
|
error = ext4_xattr_check_names(IFIRST(header), is->s.end,
|
||||||
|
IFIRST(header));
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
/* Find the named attribute. */
|
/* Find the named attribute. */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче