Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b8927721 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 1f6a563e 2e81a4ee
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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,
+17 −1
Original line number Diff line number Diff line
@@ -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;
+31 −22
Original line number Diff line number Diff line
@@ -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 @@ 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 +
@@ -1390,8 +1394,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int 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;
	}

	/*
@@ -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;
@@ -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 = {
@@ -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;
@@ -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;
@@ -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;

@@ -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;
}
+1 −0
Original line number Diff line number Diff line
@@ -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 */