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

!12804 ext4: fix CVE-2024-49884

Merge Pull Request from: @ci-robot 
 
PR sync from: Baokun Li <libaokun1@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/LA5BPIQNW5UDYAAH7OKKXWVRO43OROI3/ 
Baokun Li (1):
  ext4: fix slab-use-after-free in ext4_split_extent_at()

Theodore Ts'o (1):
  ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path


-- 
2.46.1
 
https://gitee.com/src-openeuler/kernel/issues/IAYR9L 
 
Link:https://gitee.com/openeuler/kernel/pulls/12804

 

Reviewed-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parents b7d603a6 66e6c97e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -647,6 +647,7 @@ enum {
 */
#define EXT4_EX_NOCACHE				0x40000000
#define EXT4_EX_FORCE_CACHE			0x20000000
#define EXT4_EX_NOFAIL				0x10000000

/*
 * Flags used by ext4_free_blocks
+53 −11
Original line number Diff line number Diff line
@@ -315,11 +315,14 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
{
	struct ext4_ext_path *path = *ppath;
	int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext);
	int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO;

	if (nofail)
		flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;

	return ext4_split_extent_at(handle, inode, ppath, lblk, unwritten ?
			EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
			EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO |
			(nofail ? EXT4_GET_BLOCKS_METADATA_NOFAIL:0));
			flags);
}

/*
@@ -581,9 +584,13 @@ __read_extent_tree_block(const char *function, unsigned int line,
	struct buffer_head		*bh;
	int				err;
	ext4_fsblk_t			pblk;
	gfp_t				gfp_flags = __GFP_MOVABLE | GFP_NOFS;

	if (flags & EXT4_EX_NOFAIL)
		gfp_flags |= __GFP_NOFAIL;

	pblk = ext4_idx_pblock(idx);
	bh = sb_getblk_gfp(inode->i_sb, pblk, __GFP_MOVABLE | GFP_NOFS);
	bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
	if (unlikely(!bh))
		return ERR_PTR(-ENOMEM);

@@ -935,6 +942,10 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
	struct ext4_ext_path *path = orig_path ? *orig_path : NULL;
	short int depth, i, ppos = 0;
	int ret;
	gfp_t gfp_flags = GFP_NOFS;

	if (flags & EXT4_EX_NOFAIL)
		gfp_flags |= __GFP_NOFAIL;

	eh = ext_inode_hdr(inode);
	depth = ext_depth(inode);
@@ -955,7 +966,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
	if (!path) {
		/* account possible depth increase */
		path = kcalloc(depth + 2, sizeof(struct ext4_ext_path),
				GFP_NOFS);
				gfp_flags);
		if (unlikely(!path))
			return ERR_PTR(-ENOMEM);
		path[0].p_maxdepth = depth + 1;
@@ -1104,9 +1115,13 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
	ext4_fsblk_t newblock, oldblock;
	__le32 border;
	ext4_fsblk_t *ablocks = NULL; /* array of allocated blocks */
	gfp_t gfp_flags = GFP_NOFS;
	int err = 0;
	size_t ext_size = 0;

	if (flags & EXT4_EX_NOFAIL)
		gfp_flags |= __GFP_NOFAIL;

	/* make decision: where to split? */
	/* FIXME: now decision is simplest: at current extent */

@@ -1140,7 +1155,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
	 * We need this to handle errors and free blocks
	 * upon them.
	 */
	ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), GFP_NOFS);
	ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), gfp_flags);
	if (!ablocks)
		return -ENOMEM;

@@ -2127,7 +2142,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
	if (next != EXT_MAX_BLOCKS) {
		ext_debug("next leaf block - %u\n", next);
		BUG_ON(npath != NULL);
		npath = ext4_find_extent(inode, next, NULL, 0);
		npath = ext4_find_extent(inode, next, NULL, gb_flags);
		if (IS_ERR(npath))
			return PTR_ERR(npath);
		BUG_ON(npath->p_depth != path->p_depth);
@@ -2944,7 +2959,8 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
		ext4_fsblk_t pblk;

		/* find extent for or closest extent to this block */
		path = ext4_find_extent(inode, end, NULL, EXT4_EX_NOCACHE);
		path = ext4_find_extent(inode, end, NULL,
					EXT4_EX_NOCACHE | EXT4_EX_NOFAIL);
		if (IS_ERR(path)) {
			ext4_journal_stop(handle);
			return PTR_ERR(path);
@@ -3026,7 +3042,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
				le16_to_cpu(path[k].p_hdr->eh_entries)+1;
	} else {
		path = kcalloc(depth + 1, sizeof(struct ext4_ext_path),
			       GFP_NOFS);
			       GFP_NOFS | __GFP_NOFAIL);
		if (path == NULL) {
			ext4_journal_stop(handle);
			return -ENOMEM;
@@ -3349,6 +3365,25 @@ static int ext4_split_extent_at(handle_t *handle,
		goto out;
	}

	/*
	 * Update path is required because previous ext4_ext_insert_extent()
	 * may have freed or reallocated the path. Using EXT4_EX_NOFAIL
	 * guarantees that ext4_find_extent() will not return -ENOMEM,
	 * otherwise -ENOMEM will cause a retry in do_writepages(), and a
	 * WARN_ON may be triggered in ext4_da_update_reserve_space() due to
	 * an incorrect ee_len causing the i_reserved_data_blocks exception.
	 */
	path = ext4_find_extent(inode, ee_block, ppath,
				flags | EXT4_EX_NOFAIL);
	if (IS_ERR(path)) {
		EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld",
				 split, PTR_ERR(path));
		return PTR_ERR(path);
	}
	depth = ext_depth(inode);
	ex = path[depth].p_ext;
	*ppath = path;

	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
			if (split_flag & EXT4_EXT_DATA_VALID1) {
@@ -3397,7 +3432,7 @@ static int ext4_split_extent_at(handle_t *handle,
	ext4_ext_dirty(handle, inode, path + path->p_depth);
	return err;
out:
	ext4_ext_show_leaf(inode, path);
	ext4_ext_show_leaf(inode, *ppath);
	return err;
}

@@ -3453,7 +3488,7 @@ static int ext4_split_extent(handle_t *handle,
	 * Update path is required because previous ext4_split_extent_at() may
	 * result in split of original leaf or extent zeroout.
	 */
	path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
	path = ext4_find_extent(inode, map->m_lblk, ppath, flags);
	if (IS_ERR(path))
		return PTR_ERR(path);
	depth = ext_depth(inode);
@@ -4747,7 +4782,14 @@ int ext4_ext_truncate(handle_t *handle, struct inode *inode)
	}
	if (err)
		return err;
	return ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
retry_remove_space:
	err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
	if (err == -ENOMEM) {
		cond_resched();
		congestion_wait(BLK_RW_ASYNC, HZ/50);
		goto retry_remove_space;
	}
	return err;
}

static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,