All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054
@ 2021-12-20 15:36 Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 1/3] ext4: prevent partial update of the extent blocks Theodore Ts'o
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Theodore Ts'o @ 2021-12-20 15:36 UTC (permalink / raw)
  To: stable; +Cc: Theodore Ts'o

The following commits were cherry-picked from upstream to fix a crash
in ext4/054.  There was a minor fix-up needed in the first patch,
although no other changes were needed for the 2nd and 3rd patches.

Ext4/054 will cause older LTS kernels to crash, but unfortunately
these patches will need further back-porting to apply to 5.4, never
mind older LTS kernels.  But at least here are the fixes for 5.10....

Zhang Yi (3):
  ext4: prevent partial update of the extent blocks
  ext4: check for out-of-order index extents in
    ext4_valid_extent_entries()
  ext4: check for inconsistent extents between index and leaf block

 fs/ext4/extents.c | 93 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 63 insertions(+), 30 deletions(-)

-- 
2.31.0


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

* [PATCH 1/3] ext4: prevent partial update of the extent blocks
  2021-12-20 15:36 [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Theodore Ts'o
@ 2021-12-20 15:36 ` Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 5.10 2/3] ext4: check for out-of-order index extents in ext4_valid_extent_entries() Theodore Ts'o
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2021-12-20 15:36 UTC (permalink / raw)
  To: stable; +Cc: Zhang Yi, Theodore Ts'o

From: Zhang Yi <yi.zhang@huawei.com>

Commit 0f2f87d51aebcf71a709b52f661d681594c7dffa upstream.

In the most error path of current extents updating operations are not
roll back partial updates properly when some bad things happens(.e.g in
ext4_ext_insert_extent()). So we may get an inconsistent extents tree
if journal has been aborted due to IO error, which may probability lead
to BUGON later when we accessing these extent entries in errors=continue
mode. This patch drop extent buffer's verify flag before updatng the
contents in ext4_ext_get_access(), and reset it after updating in
__ext4_ext_dirty(). After this patch we could force to check the extent
buffer if extents tree updating was break off, make sure the extents are
consistent.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Link: https://lore.kernel.org/r/20210908120850.4012324-4-yi.zhang@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/extents.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index aa4d74f9d162..f2e569e9701d 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -136,14 +136,24 @@ int ext4_datasem_ensure_credits(handle_t *handle, struct inode *inode,
 static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
 				struct ext4_ext_path *path)
 {
+	int err = 0;
+
 	if (path->p_bh) {
 		/* path points to block */
 		BUFFER_TRACE(path->p_bh, "get_write_access");
-		return ext4_journal_get_write_access(handle, path->p_bh);
+		err = ext4_journal_get_write_access(handle, path->p_bh);
+		/*
+		 * The extent buffer's verified bit will be set again in
+		 * __ext4_ext_dirty(). We could leave an inconsistent
+		 * buffer if the extents updating procudure break off du
+		 * to some error happens, force to check it again.
+		 */
+		if (!err)
+			clear_buffer_verified(path->p_bh);
 	}
 	/* path points to leaf/index in inode body */
 	/* we use in-core data, no need to protect them */
-	return 0;
+	return err;
 }
 
 /*
@@ -164,6 +174,9 @@ static int __ext4_ext_dirty(const char *where, unsigned int line,
 		/* path points to block */
 		err = __ext4_handle_dirty_metadata(where, line, handle,
 						   inode, path->p_bh);
+		/* Extents updating done, re-set verified flag */
+		if (!err)
+			set_buffer_verified(path->p_bh);
 	} else {
 		/* path points to leaf/index in inode body */
 		err = ext4_mark_inode_dirty(handle, inode);
-- 
2.31.0


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

* [PATCH 5.10 2/3] ext4: check for out-of-order index extents in ext4_valid_extent_entries()
  2021-12-20 15:36 [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 1/3] ext4: prevent partial update of the extent blocks Theodore Ts'o
@ 2021-12-20 15:36 ` Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 5.10 3/3] ext4: check for inconsistent extents between index and leaf block Theodore Ts'o
  2021-12-22 12:02 ` [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Greg KH
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2021-12-20 15:36 UTC (permalink / raw)
  To: stable; +Cc: Zhang Yi, Theodore Ts'o

From: Zhang Yi <yi.zhang@huawei.com>

Commit 8dd27fecede55e8a4e67eef2878040ecad0f0d33 upstream.

After commit 5946d089379a ("ext4: check for overlapping extents in
ext4_valid_extent_entries()"), we can check out the overlapping extent
entry in leaf extent blocks. But the out-of-order extent entry in index
extent blocks could also trigger bad things if the filesystem is
inconsistent. So this patch add a check to figure out the out-of-order
index extents and return error.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Link: https://lore.kernel.org/r/20210908120850.4012324-2-yi.zhang@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/extents.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index f2e569e9701d..35648dc3ff50 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -369,6 +369,9 @@ static int ext4_valid_extent_entries(struct inode *inode,
 				     ext4_fsblk_t *pblk, int depth)
 {
 	unsigned short entries;
+	ext4_lblk_t lblock = 0;
+	ext4_lblk_t prev = 0;
+
 	if (eh->eh_entries == 0)
 		return 1;
 
@@ -377,31 +380,35 @@ static int ext4_valid_extent_entries(struct inode *inode,
 	if (depth == 0) {
 		/* leaf entries */
 		struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
-		ext4_lblk_t lblock = 0;
-		ext4_lblk_t prev = 0;
-		int len = 0;
 		while (entries) {
 			if (!ext4_valid_extent(inode, ext))
 				return 0;
 
 			/* Check for overlapping extents */
 			lblock = le32_to_cpu(ext->ee_block);
-			len = ext4_ext_get_actual_len(ext);
 			if ((lblock <= prev) && prev) {
 				*pblk = ext4_ext_pblock(ext);
 				return 0;
 			}
+			prev = lblock + ext4_ext_get_actual_len(ext) - 1;
 			ext++;
 			entries--;
-			prev = lblock + len - 1;
 		}
 	} else {
 		struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
 		while (entries) {
 			if (!ext4_valid_extent_idx(inode, ext_idx))
 				return 0;
+
+			/* Check for overlapping index extents */
+			lblock = le32_to_cpu(ext_idx->ei_block);
+			if ((lblock <= prev) && prev) {
+				*pblk = ext4_idx_pblock(ext_idx);
+				return 0;
+			}
 			ext_idx++;
 			entries--;
+			prev = lblock;
 		}
 	}
 	return 1;
-- 
2.31.0


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

* [PATCH 5.10 3/3] ext4: check for inconsistent extents between index and leaf block
  2021-12-20 15:36 [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 1/3] ext4: prevent partial update of the extent blocks Theodore Ts'o
  2021-12-20 15:36 ` [PATCH 5.10 2/3] ext4: check for out-of-order index extents in ext4_valid_extent_entries() Theodore Ts'o
@ 2021-12-20 15:36 ` Theodore Ts'o
  2021-12-22 12:02 ` [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Greg KH
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2021-12-20 15:36 UTC (permalink / raw)
  To: stable; +Cc: Zhang Yi, Theodore Ts'o

From: Zhang Yi <yi.zhang@huawei.com>

Now that we can check out overlapping extents in leaf block and
out-of-order index extents in index block. But the .ee_block in the
first extent of one leaf block should equal to the .ei_block in it's
parent index extent entry. This patch add a check to verify such
inconsistent between the index and leaf block.

Commit 9c6e071913792d80894cd0be98cc3c4b770e26d3 upstream.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Link: https://lore.kernel.org/r/20210908120850.4012324-3-yi.zhang@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/extents.c | 59 +++++++++++++++++++++++++++++------------------
 1 file changed, 36 insertions(+), 23 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 35648dc3ff50..618675a41efb 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -366,7 +366,8 @@ static int ext4_valid_extent_idx(struct inode *inode,
 
 static int ext4_valid_extent_entries(struct inode *inode,
 				     struct ext4_extent_header *eh,
-				     ext4_fsblk_t *pblk, int depth)
+				     ext4_lblk_t lblk, ext4_fsblk_t *pblk,
+				     int depth)
 {
 	unsigned short entries;
 	ext4_lblk_t lblock = 0;
@@ -380,6 +381,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
 	if (depth == 0) {
 		/* leaf entries */
 		struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
+
+		/*
+		 * The logical block in the first entry should equal to
+		 * the number in the index block.
+		 */
+		if (depth != ext_depth(inode) &&
+		    lblk != le32_to_cpu(ext->ee_block))
+			return 0;
 		while (entries) {
 			if (!ext4_valid_extent(inode, ext))
 				return 0;
@@ -396,6 +405,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
 		}
 	} else {
 		struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
+
+		/*
+		 * The logical block in the first entry should equal to
+		 * the number in the parent index block.
+		 */
+		if (depth != ext_depth(inode) &&
+		    lblk != le32_to_cpu(ext_idx->ei_block))
+			return 0;
 		while (entries) {
 			if (!ext4_valid_extent_idx(inode, ext_idx))
 				return 0;
@@ -416,7 +433,7 @@ static int ext4_valid_extent_entries(struct inode *inode,
 
 static int __ext4_ext_check(const char *function, unsigned int line,
 			    struct inode *inode, struct ext4_extent_header *eh,
-			    int depth, ext4_fsblk_t pblk)
+			    int depth, ext4_fsblk_t pblk, ext4_lblk_t lblk)
 {
 	const char *error_msg;
 	int max = 0, err = -EFSCORRUPTED;
@@ -442,7 +459,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
 		error_msg = "invalid eh_entries";
 		goto corrupted;
 	}
-	if (!ext4_valid_extent_entries(inode, eh, &pblk, depth)) {
+	if (!ext4_valid_extent_entries(inode, eh, lblk, &pblk, depth)) {
 		error_msg = "invalid extent entries";
 		goto corrupted;
 	}
@@ -472,7 +489,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
 }
 
 #define ext4_ext_check(inode, eh, depth, pblk)			\
-	__ext4_ext_check(__func__, __LINE__, (inode), (eh), (depth), (pblk))
+	__ext4_ext_check(__func__, __LINE__, (inode), (eh), (depth), (pblk), 0)
 
 int ext4_ext_check_inode(struct inode *inode)
 {
@@ -505,16 +522,18 @@ static void ext4_cache_extents(struct inode *inode,
 
 static struct buffer_head *
 __read_extent_tree_block(const char *function, unsigned int line,
-			 struct inode *inode, ext4_fsblk_t pblk, int depth,
-			 int flags)
+			 struct inode *inode, struct ext4_extent_idx *idx,
+			 int depth, int flags)
 {
 	struct buffer_head		*bh;
 	int				err;
 	gfp_t				gfp_flags = __GFP_MOVABLE | GFP_NOFS;
+	ext4_fsblk_t			pblk;
 
 	if (flags & EXT4_EX_NOFAIL)
 		gfp_flags |= __GFP_NOFAIL;
 
+	pblk = ext4_idx_pblock(idx);
 	bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
 	if (unlikely(!bh))
 		return ERR_PTR(-ENOMEM);
@@ -527,8 +546,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
 	}
 	if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE))
 		return bh;
-	err = __ext4_ext_check(function, line, inode,
-			       ext_block_hdr(bh), depth, pblk);
+	err = __ext4_ext_check(function, line, inode, ext_block_hdr(bh),
+			       depth, pblk, le32_to_cpu(idx->ei_block));
 	if (err)
 		goto errout;
 	set_buffer_verified(bh);
@@ -546,8 +565,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
 
 }
 
-#define read_extent_tree_block(inode, pblk, depth, flags)		\
-	__read_extent_tree_block(__func__, __LINE__, (inode), (pblk),   \
+#define read_extent_tree_block(inode, idx, depth, flags)		\
+	__read_extent_tree_block(__func__, __LINE__, (inode), (idx),	\
 				 (depth), (flags))
 
 /*
@@ -597,8 +616,7 @@ int ext4_ext_precache(struct inode *inode)
 			i--;
 			continue;
 		}
-		bh = read_extent_tree_block(inode,
-					    ext4_idx_pblock(path[i].p_idx++),
+		bh = read_extent_tree_block(inode, path[i].p_idx++,
 					    depth - i - 1,
 					    EXT4_EX_FORCE_CACHE);
 		if (IS_ERR(bh)) {
@@ -903,8 +921,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
 		path[ppos].p_depth = i;
 		path[ppos].p_ext = NULL;
 
-		bh = read_extent_tree_block(inode, path[ppos].p_block, --i,
-					    flags);
+		bh = read_extent_tree_block(inode, path[ppos].p_idx, --i, flags);
 		if (IS_ERR(bh)) {
 			ret = PTR_ERR(bh);
 			goto err;
@@ -1509,7 +1526,6 @@ static int ext4_ext_search_right(struct inode *inode,
 	struct ext4_extent_header *eh;
 	struct ext4_extent_idx *ix;
 	struct ext4_extent *ex;
-	ext4_fsblk_t block;
 	int depth;	/* Note, NOT eh_depth; depth from top of tree */
 	int ee_len;
 
@@ -1576,20 +1592,17 @@ static int ext4_ext_search_right(struct inode *inode,
 	 * follow it and find the closest allocated
 	 * block to the right */
 	ix++;
-	block = ext4_idx_pblock(ix);
 	while (++depth < path->p_depth) {
 		/* subtract from p_depth to get proper eh_depth */
-		bh = read_extent_tree_block(inode, block,
-					    path->p_depth - depth, 0);
+		bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		eh = ext_block_hdr(bh);
 		ix = EXT_FIRST_INDEX(eh);
-		block = ext4_idx_pblock(ix);
 		put_bh(bh);
 	}
 
-	bh = read_extent_tree_block(inode, block, path->p_depth - depth, 0);
+	bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 	eh = ext_block_hdr(bh);
@@ -2968,9 +2981,9 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			ext_debug(inode, "move to level %d (block %llu)\n",
 				  i + 1, ext4_idx_pblock(path[i].p_idx));
 			memset(path + i + 1, 0, sizeof(*path));
-			bh = read_extent_tree_block(inode,
-				ext4_idx_pblock(path[i].p_idx), depth - i - 1,
-				EXT4_EX_NOCACHE);
+			bh = read_extent_tree_block(inode, path[i].p_idx,
+						    depth - i - 1,
+						    EXT4_EX_NOCACHE);
 			if (IS_ERR(bh)) {
 				/* should we reset i_size? */
 				err = PTR_ERR(bh);
-- 
2.31.0


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

* Re: [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054
  2021-12-20 15:36 [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Theodore Ts'o
                   ` (2 preceding siblings ...)
  2021-12-20 15:36 ` [PATCH 5.10 3/3] ext4: check for inconsistent extents between index and leaf block Theodore Ts'o
@ 2021-12-22 12:02 ` Greg KH
  3 siblings, 0 replies; 5+ messages in thread
From: Greg KH @ 2021-12-22 12:02 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: stable

On Mon, Dec 20, 2021 at 10:36:56AM -0500, Theodore Ts'o wrote:
> The following commits were cherry-picked from upstream to fix a crash
> in ext4/054.  There was a minor fix-up needed in the first patch,
> although no other changes were needed for the 2nd and 3rd patches.
> 
> Ext4/054 will cause older LTS kernels to crash, but unfortunately
> these patches will need further back-porting to apply to 5.4, never
> mind older LTS kernels.  But at least here are the fixes for 5.10....
> 
> Zhang Yi (3):
>   ext4: prevent partial update of the extent blocks
>   ext4: check for out-of-order index extents in
>     ext4_valid_extent_entries()
>   ext4: check for inconsistent extents between index and leaf block
> 
>  fs/ext4/extents.c | 93 ++++++++++++++++++++++++++++++++---------------
>  1 file changed, 63 insertions(+), 30 deletions(-)
> 
> -- 
> 2.31.0
> 

I also had to apply them to 5.15.y as you do not want people to upgrade
and have a regression.

thanks,

greg k-h

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

end of thread, other threads:[~2021-12-22 12:08 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-20 15:36 [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Theodore Ts'o
2021-12-20 15:36 ` [PATCH 1/3] ext4: prevent partial update of the extent blocks Theodore Ts'o
2021-12-20 15:36 ` [PATCH 5.10 2/3] ext4: check for out-of-order index extents in ext4_valid_extent_entries() Theodore Ts'o
2021-12-20 15:36 ` [PATCH 5.10 3/3] ext4: check for inconsistent extents between index and leaf block Theodore Ts'o
2021-12-22 12:02 ` [PATCH 5.10 0/3] Fix kernel crash caused by ext4/054 Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.