stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 5.4 0/2] CVE-2024-49884
@ 2025-01-25  0:31 Shaoying Xu
  2025-01-25  0:31 ` [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path Shaoying Xu
  2025-01-25  0:31 ` [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at() Shaoying Xu
  0 siblings, 2 replies; 5+ messages in thread
From: Shaoying Xu @ 2025-01-25  0:31 UTC (permalink / raw)
  To: stable; +Cc: shaoyi

Backport CVE-2024-49884 fixes to stable 5.4.

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

 fs/ext4/ext4.h    |  1 +
 fs/ext4/extents.c | 64 +++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 54 insertions(+), 11 deletions(-)

-- 
2.40.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path
  2025-01-25  0:31 [PATCH 5.4 0/2] CVE-2024-49884 Shaoying Xu
@ 2025-01-25  0:31 ` Shaoying Xu
  2025-01-25 14:03   ` Sasha Levin
  2025-01-25  0:31 ` [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at() Shaoying Xu
  1 sibling, 1 reply; 5+ messages in thread
From: Shaoying Xu @ 2025-01-25  0:31 UTC (permalink / raw)
  To: stable; +Cc: shaoyi, Theodore Ts'o, Anna Pendleton, Harshad Shirwadkar

From: Theodore Ts'o <tytso@mit.edu>

[ Upstream commit 73c384c0cdaa8ea9ca9ef2d0cff6a25930f1648e ]

We can't fail in the truncate path without requiring an fsck.
Add work around for this by using a combination of retry loops
and the __GFP_NOFAIL flag.

From: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Anna Pendleton <pendleton@google.com>
Reviewed-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com>
Link: https://lore.kernel.org/r/20200507175028.15061-1-pendleton@google.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Stable-dep-of: c26ab35702f8 ("ext4: fix slab-use-after-free in ext4_split_extent_at()")
[v5.4: resolved contextual conflict in __read_extent_tree_block]
Signed-off-by: Shaoying Xu <shaoyi@amazon.com>
---
 fs/ext4/ext4.h    |  1 +
 fs/ext4/extents.c | 43 +++++++++++++++++++++++++++++++++----------
 2 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 4d02116193de..44bfa589ed36 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -628,6 +628,7 @@ enum {
  */
 #define EXT4_EX_NOCACHE				0x40000000
 #define EXT4_EX_FORCE_CACHE			0x20000000
+#define EXT4_EX_NOFAIL				0x10000000
 
 /*
  * Flags used by ext4_free_blocks
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 0d692025f923..0e16e7c08a42 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -304,11 +304,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);
 }
 
 /*
@@ -572,9 +575,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);
 
@@ -919,6 +926,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);
@@ -939,7 +950,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;
@@ -1088,9 +1099,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 */
 
@@ -1124,7 +1139,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;
 
@@ -2110,7 +2125,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);
@@ -3018,7 +3033,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);
@@ -3104,7 +3120,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;
@@ -3528,7 +3544,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);
@@ -4650,7 +4666,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,
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2025-01-25  0:31 [PATCH 5.4 0/2] CVE-2024-49884 Shaoying Xu
  2025-01-25  0:31 ` [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path Shaoying Xu
@ 2025-01-25  0:31 ` Shaoying Xu
  2025-01-25 14:03   ` Sasha Levin
  1 sibling, 1 reply; 5+ messages in thread
From: Shaoying Xu @ 2025-01-25  0:31 UTC (permalink / raw)
  To: stable
  Cc: shaoyi, Baokun Li, stable, Jan Kara, Ojaswin Mujoo,
	Theodore Ts'o

From: Baokun Li <libaokun1@huawei.com>

[ Upstream commit c26ab35702f8cd0cdc78f96aa5856bfb77be798f ]

We hit the following use-after-free:

==================================================================
BUG: KASAN: slab-use-after-free in ext4_split_extent_at+0xba8/0xcc0
Read of size 2 at addr ffff88810548ed08 by task kworker/u20:0/40
CPU: 0 PID: 40 Comm: kworker/u20:0 Not tainted 6.9.0-dirty #724
Call Trace:
 <TASK>
 kasan_report+0x93/0xc0
 ext4_split_extent_at+0xba8/0xcc0
 ext4_split_extent.isra.0+0x18f/0x500
 ext4_split_convert_extents+0x275/0x750
 ext4_ext_handle_unwritten_extents+0x73e/0x1580
 ext4_ext_map_blocks+0xe20/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]

Allocated by task 40:
 __kmalloc_noprof+0x1ac/0x480
 ext4_find_extent+0xf3b/0x1e70
 ext4_ext_map_blocks+0x188/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]

Freed by task 40:
 kfree+0xf1/0x2b0
 ext4_find_extent+0xa71/0x1e70
 ext4_ext_insert_extent+0xa22/0x3260
 ext4_split_extent_at+0x3ef/0xcc0
 ext4_split_extent.isra.0+0x18f/0x500
 ext4_split_convert_extents+0x275/0x750
 ext4_ext_handle_unwritten_extents+0x73e/0x1580
 ext4_ext_map_blocks+0xe20/0x2dc0
 ext4_map_blocks+0x724/0x1700
 ext4_do_writepages+0x12d6/0x2a70
[...]
==================================================================

The flow of issue triggering is as follows:

ext4_split_extent_at
  path = *ppath
  ext4_ext_insert_extent(ppath)
    ext4_ext_create_new_leaf(ppath)
      ext4_find_extent(orig_path)
        path = *orig_path
        read_extent_tree_block
          // return -ENOMEM or -EIO
        ext4_free_ext_path(path)
          kfree(path)
        *orig_path = NULL
  a. If err is -ENOMEM:
  ext4_ext_dirty(path + path->p_depth)
  // path use-after-free !!!
  b. If err is -EIO and we have EXT_DEBUG defined:
  ext4_ext_show_leaf(path)
    eh = path[depth].p_hdr
    // path also use-after-free !!!

So when trying to zeroout or fix the extent length, call ext4_find_extent()
to update the path.

In addition we use *ppath directly as an ext4_ext_show_leaf() input to
avoid possible use-after-free when EXT_DEBUG is defined, and to avoid
unnecessary path updates.

Fixes: dfe5080939ea ("ext4: drop EXT4_EX_NOFREE_ON_ERR from rest of extents handling code")
Cc: stable@kernel.org
Signed-off-by: Baokun Li <libaokun1@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Tested-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Link: https://patch.msgid.link/20240822023545.1994557-4-libaokun@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Shaoying Xu <shaoyi@amazon.com>
---
 fs/ext4/extents.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 0e16e7c08a42..5c0ef7a04169 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3440,6 +3440,25 @@ static int ext4_split_extent_at(handle_t *handle,
 	if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM)
 		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) {
@@ -3488,7 +3507,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;
 }
 
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path
  2025-01-25  0:31 ` [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path Shaoying Xu
@ 2025-01-25 14:03   ` Sasha Levin
  0 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2025-01-25 14:03 UTC (permalink / raw)
  To: stable; +Cc: Shaoying Xu, Sasha Levin

[ Sasha's backport helper bot ]

Hi,

The upstream commit SHA1 provided is correct: 73c384c0cdaa8ea9ca9ef2d0cff6a25930f1648e

WARNING: Author mismatch between patch and upstream commit:
Backport author: Shaoying Xu<shaoyi@amazon.com>
Commit author: Theodore Ts'o<tytso@mit.edu>


Status in newer kernel trees:
6.12.y | Present (exact SHA1)
6.6.y | Present (exact SHA1)
6.1.y | Present (exact SHA1)
5.15.y | Present (exact SHA1)
5.10.y | Present (exact SHA1)
5.4.y | Not found

Note: The patch differs from the upstream commit:
---
1:  73c384c0cdaa8 ! 1:  ad990516172ca ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path
    @@ Metadata
      ## Commit message ##
         ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path
     
    +    [ Upstream commit 73c384c0cdaa8ea9ca9ef2d0cff6a25930f1648e ]
    +
         We can't fail in the truncate path without requiring an fsck.
         Add work around for this by using a combination of retry loops
         and the __GFP_NOFAIL flag.
    @@ Commit message
         Reviewed-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com>
         Link: https://lore.kernel.org/r/20200507175028.15061-1-pendleton@google.com
         Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    +    Stable-dep-of: c26ab35702f8 ("ext4: fix slab-use-after-free in ext4_split_extent_at()")
    +    [v5.4: resolved contextual conflict in __read_extent_tree_block]
    +    Signed-off-by: Shaoying Xu <shaoyi@amazon.com>
     
      ## fs/ext4/ext4.h ##
     @@ fs/ext4/ext4.h: enum {
    @@ fs/ext4/extents.c: ext4_force_split_extent_at(handle_t *handle, struct inode *in
     +			flags);
      }
      
    - static int
    + /*
     @@ fs/ext4/extents.c: __read_extent_tree_block(const char *function, unsigned int line,
    - {
      	struct buffer_head		*bh;
      	int				err;
    -+	gfp_t				gfp_flags = __GFP_MOVABLE | GFP_NOFS;
    + 	ext4_fsblk_t			pblk;
    ++	gfp_t                           gfp_flags = __GFP_MOVABLE | GFP_NOFS;
     +
     +	if (flags & EXT4_EX_NOFAIL)
    -+		gfp_flags |= __GFP_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))
---

Results of testing on various branches:

| Branch                    | Patch Apply | Build Test |
|---------------------------|-------------|------------|
| stable/linux-5.4.y        |  Success    |  Success   |

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at()
  2025-01-25  0:31 ` [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at() Shaoying Xu
@ 2025-01-25 14:03   ` Sasha Levin
  0 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2025-01-25 14:03 UTC (permalink / raw)
  To: stable; +Cc: Shaoying Xu, Sasha Levin

[ Sasha's backport helper bot ]

Hi,

The upstream commit SHA1 provided is correct: c26ab35702f8cd0cdc78f96aa5856bfb77be798f

WARNING: Author mismatch between patch and upstream commit:
Backport author: Shaoying Xu<shaoyi@amazon.com>
Commit author: Baokun Li<libaokun1@huawei.com>


Status in newer kernel trees:
6.12.y | Present (exact SHA1)
6.6.y | Present (different SHA1: 8fe117790b37)
6.1.y | Present (different SHA1: a5401d4c3e2a)
5.15.y | Present (different SHA1: cafcc1bd6293)
5.10.y | Present (different SHA1: e52f933598b7)
5.4.y | Not found

Note: The patch differs from the upstream commit:
---
1:  c26ab35702f8c ! 1:  2df1566f67dbb ext4: fix slab-use-after-free in ext4_split_extent_at()
    @@ Metadata
      ## Commit message ##
         ext4: fix slab-use-after-free in ext4_split_extent_at()
     
    +    [ Upstream commit c26ab35702f8cd0cdc78f96aa5856bfb77be798f ]
    +
         We hit the following use-after-free:
     
         ==================================================================
    @@ Commit message
         Tested-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
         Link: https://patch.msgid.link/20240822023545.1994557-4-libaokun@huaweicloud.com
         Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    +    Signed-off-by: Shaoying Xu <shaoyi@amazon.com>
     
      ## fs/ext4/extents.c ##
     @@ fs/ext4/extents.c: static int ext4_split_extent_at(handle_t *handle,
---

Results of testing on various branches:

| Branch                    | Patch Apply | Build Test |
|---------------------------|-------------|------------|
| stable/linux-5.4.y        |  Success    |  Success   |

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-01-25 14:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-25  0:31 [PATCH 5.4 0/2] CVE-2024-49884 Shaoying Xu
2025-01-25  0:31 ` [PATCH 5.4 1/2] ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path Shaoying Xu
2025-01-25 14:03   ` Sasha Levin
2025-01-25  0:31 ` [PATCH 5.4 2/2] ext4: fix slab-use-after-free in ext4_split_extent_at() Shaoying Xu
2025-01-25 14:03   ` Sasha Levin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).