* [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 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).