Skip to content

Commit

Permalink
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/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
  • Loading branch information
torvalds committed Aug 29, 2016
2 parents 1f6a563 + 2e81a4e commit b892772
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 25 deletions.
2 changes: 0 additions & 2 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
18 changes: 17 additions & 1 deletion fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -2241,20 +2242,35 @@ 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 "
"(block %llu)!", i, block_bitmap);
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 "
"(block %llu)!", i, inode_bitmap);
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: "
Expand Down Expand Up @@ -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;
Expand Down
53 changes: 31 additions & 22 deletions fs/ext4/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -1382,16 +1386,15 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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 +
EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
(void *)header, total_ino,
inode->i_sb->s_blocksize);
EXT4_I(inode)->i_extra_isize = new_extra_isize;
error = 0;
goto cleanup;
goto out;
}

/*
Expand All @@ -1414,7 +1417,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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;
Expand All @@ -1428,7 +1431,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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 = {
Expand Down Expand Up @@ -1459,7 +1462,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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;
Expand Down Expand Up @@ -1514,22 +1517,22 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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;
Expand All @@ -1551,6 +1554,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
kfree(bs);
}
brelse(bh);
out:
ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
up_write(&EXT4_I(inode)->xattr_sem);
return 0;

Expand All @@ -1562,6 +1567,10 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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;
}
Expand Down
1 change: 1 addition & 0 deletions fs/ext4/xattr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit b892772

Please sign in to comment.