Fix bugs that could cause kernel deadlocks or file system corruption
while moving xattrs to expand the extended inode. Also add some sanity checks to the block group descriptors to make sure we don't end up overwriting the superblock. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJXw7i2AAoJEPL5WVaVDYGj96gH/A8rNgx7BoqPx3kanVEamblT tM0X9JcEGmKHN4enRts2b78EWbR0/U0SOP92+fg9SSq2MDJ0/kdaKLWmbUwx8jUi B7HMEqCprlCdigK7wwt3xF+6edyZRhtzlWy3bhxJ40f0KT5CuriSQbxogr931uKl hUKW2h5JtUqHtINzTt4oWjVm8xwrScxuYHYAcpw0G42ZzfO6xQOzQdowcx4m3cE9 PrtTbU5MwW8/wgsdLiClScQq30MK/GCbHh5heyRt1BcNo9+MDsZDOgdavh9StfnW Bl1N6zwRtRBJNcpKWfTfwU4NTIvStCTyA8BJgKgE95YIHDsstJVl4MO7ot25qbM= =pXe+ -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 fixes from Ted Ts'o: "Fix bugs that could cause kernel deadlocks or file system corruption while moving xattrs to expand the extended inode. Also add some sanity checks to the block group descriptors to make sure we don't end up overwriting the superblock" * tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: avoid deadlock when expanding inode size ext4: properly align shifted xattrs when expanding inodes ext4: fix xattr shifting when expanding inodes part 2 ext4: fix xattr shifting when expanding inodes ext4: validate that metadata blocks do not overlap superblock ext4: reserve xattr index for the Hurd
This commit is contained in:
commit
b8927721ae
@ -5466,8 +5466,6 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
|
||||
sbi->s_want_extra_isize,
|
||||
iloc, handle);
|
||||
if (ret) {
|
||||
ext4_set_inode_state(inode,
|
||||
EXT4_STATE_NO_EXPAND);
|
||||
if (mnt_count !=
|
||||
le16_to_cpu(sbi->s_es->s_mnt_count)) {
|
||||
ext4_warning(inode->i_sb,
|
||||
|
@ -2211,6 +2211,7 @@ void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group,
|
||||
|
||||
/* Called at mount-time, super-block is locked */
|
||||
static int ext4_check_descriptors(struct super_block *sb,
|
||||
ext4_fsblk_t sb_block,
|
||||
ext4_group_t *first_not_zeroed)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
@ -2241,6 +2242,11 @@ static int ext4_check_descriptors(struct super_block *sb,
|
||||
grp = i;
|
||||
|
||||
block_bitmap = ext4_block_bitmap(sb, gdp);
|
||||
if (block_bitmap == sb_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Block bitmap for group %u overlaps "
|
||||
"superblock", i);
|
||||
}
|
||||
if (block_bitmap < first_block || block_bitmap > last_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Block bitmap for group %u not in group "
|
||||
@ -2248,6 +2254,11 @@ static int ext4_check_descriptors(struct super_block *sb,
|
||||
return 0;
|
||||
}
|
||||
inode_bitmap = ext4_inode_bitmap(sb, gdp);
|
||||
if (inode_bitmap == sb_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Inode bitmap for group %u overlaps "
|
||||
"superblock", i);
|
||||
}
|
||||
if (inode_bitmap < first_block || inode_bitmap > last_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Inode bitmap for group %u not in group "
|
||||
@ -2255,6 +2266,11 @@ static int ext4_check_descriptors(struct super_block *sb,
|
||||
return 0;
|
||||
}
|
||||
inode_table = ext4_inode_table(sb, gdp);
|
||||
if (inode_table == sb_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Inode table for group %u overlaps "
|
||||
"superblock", i);
|
||||
}
|
||||
if (inode_table < first_block ||
|
||||
inode_table + sbi->s_itb_per_group - 1 > last_block) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
@ -3757,7 +3773,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto failed_mount2;
|
||||
}
|
||||
}
|
||||
if (!ext4_check_descriptors(sb, &first_not_zeroed)) {
|
||||
if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {
|
||||
ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto failed_mount2;
|
||||
|
@ -1353,15 +1353,19 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
|
||||
size_t min_offs, free;
|
||||
int total_ino;
|
||||
void *base, *start, *end;
|
||||
int extra_isize = 0, error = 0, tried_min_extra_isize = 0;
|
||||
int error = 0, tried_min_extra_isize = 0;
|
||||
int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
|
||||
int isize_diff; /* How much do we need to grow i_extra_isize */
|
||||
|
||||
down_write(&EXT4_I(inode)->xattr_sem);
|
||||
/*
|
||||
* Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
|
||||
*/
|
||||
ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
|
||||
retry:
|
||||
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
|
||||
up_write(&EXT4_I(inode)->xattr_sem);
|
||||
return 0;
|
||||
}
|
||||
isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
|
||||
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
|
||||
goto out;
|
||||
|
||||
header = IHDR(inode, raw_inode);
|
||||
entry = IFIRST(header);
|
||||
@ -1382,7 +1386,7 @@ retry:
|
||||
goto cleanup;
|
||||
|
||||
free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
|
||||
if (free >= new_extra_isize) {
|
||||
if (free >= isize_diff) {
|
||||
entry = IFIRST(header);
|
||||
ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
|
||||
- new_extra_isize, (void *)raw_inode +
|
||||
@ -1390,8 +1394,7 @@ retry:
|
||||
(void *)header, total_ino,
|
||||
inode->i_sb->s_blocksize);
|
||||
EXT4_I(inode)->i_extra_isize = new_extra_isize;
|
||||
error = 0;
|
||||
goto cleanup;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1414,7 +1417,7 @@ retry:
|
||||
end = bh->b_data + bh->b_size;
|
||||
min_offs = end - base;
|
||||
free = ext4_xattr_free_space(first, &min_offs, base, NULL);
|
||||
if (free < new_extra_isize) {
|
||||
if (free < isize_diff) {
|
||||
if (!tried_min_extra_isize && s_min_extra_isize) {
|
||||
tried_min_extra_isize++;
|
||||
new_extra_isize = s_min_extra_isize;
|
||||
@ -1428,7 +1431,7 @@ retry:
|
||||
free = inode->i_sb->s_blocksize;
|
||||
}
|
||||
|
||||
while (new_extra_isize > 0) {
|
||||
while (isize_diff > 0) {
|
||||
size_t offs, size, entry_size;
|
||||
struct ext4_xattr_entry *small_entry = NULL;
|
||||
struct ext4_xattr_info i = {
|
||||
@ -1459,7 +1462,7 @@ retry:
|
||||
EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
|
||||
EXT4_XATTR_LEN(last->e_name_len);
|
||||
if (total_size <= free && total_size < min_total_size) {
|
||||
if (total_size < new_extra_isize) {
|
||||
if (total_size < isize_diff) {
|
||||
small_entry = last;
|
||||
} else {
|
||||
entry = last;
|
||||
@ -1514,22 +1517,22 @@ retry:
|
||||
error = ext4_xattr_ibody_set(handle, inode, &i, is);
|
||||
if (error)
|
||||
goto cleanup;
|
||||
total_ino -= entry_size;
|
||||
|
||||
entry = IFIRST(header);
|
||||
if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)
|
||||
shift_bytes = new_extra_isize;
|
||||
if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
|
||||
shift_bytes = isize_diff;
|
||||
else
|
||||
shift_bytes = entry_size + size;
|
||||
shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
|
||||
/* Adjust the offsets and shift the remaining entries ahead */
|
||||
ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
|
||||
shift_bytes, (void *)raw_inode +
|
||||
EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
|
||||
(void *)header, total_ino - entry_size,
|
||||
inode->i_sb->s_blocksize);
|
||||
ext4_xattr_shift_entries(entry, -shift_bytes,
|
||||
(void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
|
||||
EXT4_I(inode)->i_extra_isize + shift_bytes,
|
||||
(void *)header, total_ino, inode->i_sb->s_blocksize);
|
||||
|
||||
extra_isize += shift_bytes;
|
||||
new_extra_isize -= shift_bytes;
|
||||
EXT4_I(inode)->i_extra_isize = extra_isize;
|
||||
isize_diff -= shift_bytes;
|
||||
EXT4_I(inode)->i_extra_isize += shift_bytes;
|
||||
header = IHDR(inode, raw_inode);
|
||||
|
||||
i.name = b_entry_name;
|
||||
i.value = buffer;
|
||||
@ -1551,6 +1554,8 @@ retry:
|
||||
kfree(bs);
|
||||
}
|
||||
brelse(bh);
|
||||
out:
|
||||
ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
|
||||
up_write(&EXT4_I(inode)->xattr_sem);
|
||||
return 0;
|
||||
|
||||
@ -1562,6 +1567,10 @@ cleanup:
|
||||
kfree(is);
|
||||
kfree(bs);
|
||||
brelse(bh);
|
||||
/*
|
||||
* We deliberately leave EXT4_STATE_NO_EXPAND set here since inode
|
||||
* size expansion failed.
|
||||
*/
|
||||
up_write(&EXT4_I(inode)->xattr_sem);
|
||||
return error;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define EXT4_XATTR_INDEX_SYSTEM 7
|
||||
#define EXT4_XATTR_INDEX_RICHACL 8
|
||||
#define EXT4_XATTR_INDEX_ENCRYPTION 9
|
||||
#define EXT4_XATTR_INDEX_HURD 10 /* Reserved for Hurd */
|
||||
|
||||
struct ext4_xattr_header {
|
||||
__le32 h_magic; /* magic number for identification */
|
||||
|
Loading…
Reference in New Issue
Block a user