Unverified Commit 8eebadc6 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!9968 CVE-2024-40972

Merge Pull Request from: @ci-robot 
 
PR sync from: Yifan Qiao <qiaoyifan4@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/4GKDCRDLZ4CAPEJ2MNQZU25Q4HV6CH6J/ 
Jan Kara (2):
  ext4: fold quota accounting into ext4_xattr_inode_lookup_create()
  ext4: do not create EA inode under buffer lock


-- 
2.39.2
 
https://gitee.com/src-openeuler/kernel/issues/IAD03M 
 
Link:https://gitee.com/openeuler/kernel/pulls/9968

 

Reviewed-by: default avatarYang Yingliang <yangyingliang@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
parents cdbb8c63 b54fa08a
Loading
Loading
Loading
Loading
+73 −81
Original line number Diff line number Diff line
@@ -1512,45 +1512,49 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
/*
 * Add value of the EA in an inode.
 */
static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode,
					  const void *value, size_t value_len,
					  struct inode **ret_inode)
static struct inode *ext4_xattr_inode_lookup_create(handle_t *handle,
		struct inode *inode, const void *value, size_t value_len)
{
	struct inode *ea_inode;
	u32 hash;
	int err;

	/* Account inode & space to quota even if sharing... */
	err = ext4_xattr_inode_alloc_quota(inode, value_len);
	if (err)
		return ERR_PTR(err);

	hash = ext4_xattr_inode_hash(EXT4_SB(inode->i_sb), value, value_len);
	ea_inode = ext4_xattr_inode_cache_find(inode, value, value_len, hash);
	if (ea_inode) {
		err = ext4_xattr_inode_inc_ref(handle, ea_inode);
		if (err) {
			iput(ea_inode);
			return err;
		}

		*ret_inode = ea_inode;
		return 0;
		if (err)
			goto out_err;
		return ea_inode;
	}

	/* Create an inode for the EA value */
	ea_inode = ext4_xattr_inode_create(handle, inode, hash);
	if (IS_ERR(ea_inode))
		return PTR_ERR(ea_inode);
	if (IS_ERR(ea_inode)) {
		ext4_xattr_inode_free_quota(inode, NULL, value_len);
		return ea_inode;
	}

	err = ext4_xattr_inode_write(handle, ea_inode, value, value_len);
	if (err) {
		ext4_xattr_inode_dec_ref(handle, ea_inode);
		iput(ea_inode);
		return err;
		goto out_err;
	}

	if (EA_INODE_CACHE(inode))
		mb_cache_entry_create(EA_INODE_CACHE(inode), GFP_NOFS, hash,
				      ea_inode->i_ino, true /* reusable */);

	*ret_inode = ea_inode;
	return 0;
	return ea_inode;
out_err:
	iput(ea_inode);
	ext4_xattr_inode_free_quota(inode, NULL, value_len);
	return ERR_PTR(err);
}

/*
@@ -1562,6 +1566,7 @@ static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode,
static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
				struct ext4_xattr_search *s,
				handle_t *handle, struct inode *inode,
				struct inode *new_ea_inode,
				bool is_block)
{
	struct ext4_xattr_entry *last, *next;
@@ -1569,7 +1574,6 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
	size_t min_offs = s->end - s->base, name_len = strlen(i->name);
	int in_inode = i->in_inode;
	struct inode *old_ea_inode = NULL;
	struct inode *new_ea_inode = NULL;
	size_t old_size, new_size;
	int ret;

@@ -1654,43 +1658,11 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
			old_ea_inode = NULL;
			goto out;
		}
	}
	if (i->value && in_inode) {
		WARN_ON_ONCE(!i->value_len);

		ret = ext4_xattr_inode_alloc_quota(inode, i->value_len);
		if (ret)
			goto out;

		ret = ext4_xattr_inode_lookup_create(handle, inode, i->value,
						     i->value_len,
						     &new_ea_inode);
		if (ret) {
			new_ea_inode = NULL;
			ext4_xattr_inode_free_quota(inode, NULL, i->value_len);
			goto out;
		}
	}

	if (old_ea_inode) {
		/* We are ready to release ref count on the old_ea_inode. */
		ret = ext4_xattr_inode_dec_ref(handle, old_ea_inode);
		if (ret) {
			/* Release newly required ref count on new_ea_inode. */
			if (new_ea_inode) {
				int err;

				err = ext4_xattr_inode_dec_ref(handle,
							       new_ea_inode);
				if (err)
					ext4_warning_inode(new_ea_inode,
						  "dec ref new_ea_inode err=%d",
						  err);
				ext4_xattr_inode_free_quota(inode, new_ea_inode,
							    i->value_len);
			}
		if (ret)
			goto out;
		}

		ext4_xattr_inode_free_quota(inode, old_ea_inode,
					    le32_to_cpu(here->e_value_size));
@@ -1814,7 +1786,6 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
	ret = 0;
out:
	iput(old_ea_inode);
	iput(new_ea_inode);
	return ret;
}

@@ -1877,9 +1848,21 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
	size_t old_ea_inode_quota = 0;
	unsigned int ea_ino;


#define header(x) ((struct ext4_xattr_header *)(x))

	/* If we need EA inode, prepare it before locking the buffer */
	if (i->value && i->in_inode) {
		WARN_ON_ONCE(!i->value_len);

		ea_inode = ext4_xattr_inode_lookup_create(handle, inode,
					i->value, i->value_len);
		if (IS_ERR(ea_inode)) {
			error = PTR_ERR(ea_inode);
			ea_inode = NULL;
			goto cleanup;
		}
	}

	if (s->base) {
		int offset = (char *)s->here - bs->bh->b_data;

@@ -1887,6 +1870,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
		error = ext4_journal_get_write_access(handle, bs->bh);
		if (error)
			goto cleanup;

		lock_buffer(bs->bh);

		if (header(s->base)->h_refcount == cpu_to_le32(1)) {
@@ -1913,7 +1897,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
			}
			ea_bdebug(bs->bh, "modifying in-place");
			error = ext4_xattr_set_entry(i, s, handle, inode,
						     true /* is_block */);
					     ea_inode, true /* is_block */);
			ext4_xattr_block_csum_set(inode, bs->bh);
			unlock_buffer(bs->bh);
			if (error == -EFSCORRUPTED)
@@ -1982,29 +1966,13 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
		s->end = s->base + sb->s_blocksize;
	}

	error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */);
	error = ext4_xattr_set_entry(i, s, handle, inode, ea_inode,
				     true /* is_block */);
	if (error == -EFSCORRUPTED)
		goto bad_block;
	if (error)
		goto cleanup;

	if (i->value && s->here->e_value_inum) {
		/*
		 * A ref count on ea_inode has been taken as part of the call to
		 * ext4_xattr_set_entry() above. We would like to drop this
		 * extra ref but we have to wait until the xattr block is
		 * initialized and has its own ref count on the ea_inode.
		 */
		ea_ino = le32_to_cpu(s->here->e_value_inum);
		error = ext4_xattr_inode_iget(inode, ea_ino,
					      le32_to_cpu(s->here->e_hash),
					      &ea_inode);
		if (error) {
			ea_inode = NULL;
			goto cleanup;
		}
	}

inserted:
	if (!IS_LAST_ENTRY(s->first)) {
		new_bh = ext4_xattr_block_cache_find(inode, header(s->base),
@@ -2155,17 +2123,16 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,

cleanup:
	if (ea_inode) {
		if (error) {
			int error2;

			error2 = ext4_xattr_inode_dec_ref(handle, ea_inode);
			if (error2)
				ext4_warning_inode(ea_inode, "dec ref error=%d",
						   error2);

		/* If there was an error, revert the quota charge. */
		if (error)
			ext4_xattr_inode_free_quota(inode, ea_inode,
						    i_size_read(ea_inode));
		}
		iput(ea_inode);
	}
	if (ce)
@@ -2223,14 +2190,38 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
{
	struct ext4_xattr_ibody_header *header;
	struct ext4_xattr_search *s = &is->s;
	struct inode *ea_inode = NULL;
	int error;

	if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
		return -ENOSPC;

	error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */);
	if (error)
	/* If we need EA inode, prepare it before locking the buffer */
	if (i->value && i->in_inode) {
		WARN_ON_ONCE(!i->value_len);

		ea_inode = ext4_xattr_inode_lookup_create(handle, inode,
					i->value, i->value_len);
		if (IS_ERR(ea_inode))
			return PTR_ERR(ea_inode);
	}
	error = ext4_xattr_set_entry(i, s, handle, inode, ea_inode,
				     false /* is_block */);
	if (error) {
		if (ea_inode) {
			int error2;

			error2 = ext4_xattr_inode_dec_ref(handle, ea_inode);
			if (error2)
				ext4_warning_inode(ea_inode, "dec ref error=%d",
						   error2);

			ext4_xattr_inode_free_quota(inode, ea_inode,
						    i_size_read(ea_inode));
			iput(ea_inode);
		}
		return error;
	}
	header = IHDR(inode, ext4_raw_inode(&is->iloc));
	if (!IS_LAST_ENTRY(s->first)) {
		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
@@ -2239,6 +2230,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
		header->h_magic = cpu_to_le32(0);
		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
	}
	iput(ea_inode);
	return 0;
}