Unverified Commit 111f5b92 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!13052 ext4: fix CVE-2024-47701

Merge Pull Request from: @ci-robot 
 
PR sync from: Baokun Li <libaokun1@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/JA6CBJUBGQUV3LP74WF3KN5EEAZ5QPAA/ 
Eric Whitney (1):
  ext4: fix RENAME_WHITEOUT handling for inline directories

Thadeu Lima de Souza Cascardo (4):
  ext4: ext4_search_dir should return a proper error
  ext4: return error on ext4_find_inline_entry
  ext4: explicitly exit when ext4_find_inline_entry returns an error
  ext4: avoid OOB when system.data xattr changes underneath the
    filesystem


-- 
2.46.1
 
https://gitee.com/src-openeuler/kernel/issues/IAYPK6 
 
Link:https://gitee.com/openeuler/kernel/pulls/13052

 

Reviewed-by: default avatarzhangyi (F) <yi.zhang@huawei.com>
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parents 81cb8230 d42be740
Loading
Loading
Loading
Loading
+25 −10
Original line number Diff line number Diff line
@@ -1645,24 +1645,36 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
					struct ext4_dir_entry_2 **res_dir,
					int *has_inline_data)
{
	struct ext4_xattr_ibody_find is = {
		.s = { .not_found = -ENODATA, },
	};
	struct ext4_xattr_info i = {
		.name_index = EXT4_XATTR_INDEX_SYSTEM,
		.name = EXT4_XATTR_SYSTEM_DATA,
	};
	int ret;
	struct ext4_iloc iloc;
	void *inline_start;
	int inline_size;

	if (ext4_get_inode_loc(dir, &iloc))
		return NULL;
	ret = ext4_get_inode_loc(dir, &is.iloc);
	if (ret)
		return ERR_PTR(ret);

	down_read(&EXT4_I(dir)->xattr_sem);

	ret = ext4_xattr_ibody_find(dir, &i, &is);
	if (ret)
		goto out;

	if (!ext4_has_inline_data(dir)) {
		*has_inline_data = 0;
		goto out;
	}

	inline_start = (void *)ext4_raw_inode(&iloc)->i_block +
	inline_start = (void *)ext4_raw_inode(&is.iloc)->i_block +
						EXT4_INLINE_DOTDOT_SIZE;
	inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
	ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
	ret = ext4_search_dir(is.iloc.bh, inline_start, inline_size,
			      dir, fname, 0, res_dir);
	if (ret == 1)
		goto out_find;
@@ -1672,20 +1684,23 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
	if (ext4_get_inline_size(dir) == EXT4_MIN_INLINE_DATA_SIZE)
		goto out;

	inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
	inline_start = ext4_get_inline_xattr_pos(dir, &is.iloc);
	inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;

	ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
	ret = ext4_search_dir(is.iloc.bh, inline_start, inline_size,
			      dir, fname, 0, res_dir);
	if (ret == 1)
		goto out_find;

out:
	brelse(iloc.bh);
	iloc.bh = NULL;
	brelse(is.iloc.bh);
	if (ret < 0)
		is.iloc.bh = ERR_PTR(ret);
	else
		is.iloc.bh = NULL;
out_find:
	up_read(&EXT4_I(dir)->xattr_sem);
	return iloc.bh;
	return is.iloc.bh;
}

int ext4_delete_inline_entry(handle_t *handle,
+14 −11
Original line number Diff line number Diff line
@@ -1348,7 +1348,7 @@ static inline bool ext4_match(const struct ext4_filename *fname,
}

/*
 * Returns 0 if not found, -1 on failure, and 1 on success
 * Returns 0 if not found, -EFSCORRUPTED on failure, and 1 on success
 */
int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
		    struct inode *dir, struct ext4_filename *fname,
@@ -1369,7 +1369,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
			 * a full check */
			if (ext4_check_dir_entry(dir, NULL, de, bh, search_buf,
						 buf_size, offset))
				return -1;
				return -EFSCORRUPTED;
			*res_dir = de;
			return 1;
		}
@@ -1377,7 +1377,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
		de_len = ext4_rec_len_from_disk(de->rec_len,
						dir->i_sb->s_blocksize);
		if (de_len <= 0)
			return -1;
			return -EFSCORRUPTED;
		offset += de_len;
		de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
	}
@@ -1445,12 +1445,11 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
		int has_inline_data = 1;
		ret = ext4_find_inline_entry(dir, &fname, res_dir,
					     &has_inline_data);
		if (has_inline_data) {
		if (inlined)
				*inlined = 1;
			*inlined = has_inline_data;
		if (has_inline_data || IS_ERR(ret))
			goto cleanup_and_exit;
	}
	}

	if ((namelen <= 2) && (name[0] == '.') &&
	    (name[1] == '.' || name[1] == '\0')) {
@@ -1538,9 +1537,11 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
			goto cleanup_and_exit;
		} else {
			brelse(bh);
			if (i < 0)
			if (i < 0) {
				ret = ERR_PTR(i);
				goto cleanup_and_exit;
			}
		}
	next:
		if (++block >= nblocks)
			block = 0;
@@ -1593,7 +1594,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
		if (retval == 1)
			goto success;
		brelse(bh);
		if (retval == -1) {
		if (retval < 0) {
			bh = ERR_PTR(ERR_BAD_DX_DIR);
			goto errout;
		}
@@ -3566,7 +3567,8 @@ static void ext4_resetent(handle_t *handle, struct ext4_renament *ent,
	 * so the old->de may no longer valid and need to find it again
	 * before reset old inode info.
	 */
	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
				 &old.inlined);
	if (IS_ERR(old.bh))
		retval = PTR_ERR(old.bh);
	if (!old.bh)
@@ -3728,7 +3730,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
			return retval;
	}

	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
				 &old.inlined);
	if (IS_ERR(old.bh))
		return PTR_ERR(old.bh);
	/*