提交 28353d3b 编写于 作者: Z Zhang Yi 提交者: Yang Yingliang

ext4: check for inconsistent extents between index and leaf block

hulk inclusion
category: bugfix
bugzilla: 109205
CVE: NA
---------------------------

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.
Signed-off-by: NZhang Yi <yi.zhang@huawei.com>
Link: https://lore.kernel.org/r/20210908120850.4012324-3-yi.zhang@huawei.comSigned-off-by: NTheodore Ts'o <tytso@mit.edu>

conflict:
	fs/ext4/extents.c
Reviewed-by: NYang Erkun <yangerkun@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 4a6f39fe
...@@ -402,7 +402,8 @@ static int ext4_valid_extent_idx(struct inode *inode, ...@@ -402,7 +402,8 @@ static int ext4_valid_extent_idx(struct inode *inode,
static int ext4_valid_extent_entries(struct inode *inode, static int ext4_valid_extent_entries(struct inode *inode,
struct ext4_extent_header *eh, struct ext4_extent_header *eh,
ext4_fsblk_t *pblk, int depth) ext4_lblk_t lblk, ext4_fsblk_t *pblk,
int depth)
{ {
unsigned short entries; unsigned short entries;
ext4_lblk_t lblock = 0; ext4_lblk_t lblock = 0;
...@@ -416,6 +417,14 @@ static int ext4_valid_extent_entries(struct inode *inode, ...@@ -416,6 +417,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
if (depth == 0) { if (depth == 0) {
/* leaf entries */ /* leaf entries */
struct ext4_extent *ext = EXT_FIRST_EXTENT(eh); 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) { while (entries) {
if (!ext4_valid_extent(inode, ext)) if (!ext4_valid_extent(inode, ext))
return 0; return 0;
...@@ -432,6 +441,14 @@ static int ext4_valid_extent_entries(struct inode *inode, ...@@ -432,6 +441,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
} }
} else { } else {
struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh); 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) { while (entries) {
if (!ext4_valid_extent_idx(inode, ext_idx)) if (!ext4_valid_extent_idx(inode, ext_idx))
return 0; return 0;
...@@ -452,7 +469,7 @@ static int ext4_valid_extent_entries(struct inode *inode, ...@@ -452,7 +469,7 @@ static int ext4_valid_extent_entries(struct inode *inode,
static int __ext4_ext_check(const char *function, unsigned int line, static int __ext4_ext_check(const char *function, unsigned int line,
struct inode *inode, struct ext4_extent_header *eh, 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; const char *error_msg;
int max = 0, err = -EFSCORRUPTED; int max = 0, err = -EFSCORRUPTED;
...@@ -478,7 +495,7 @@ static int __ext4_ext_check(const char *function, unsigned int line, ...@@ -478,7 +495,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
error_msg = "invalid eh_entries"; error_msg = "invalid eh_entries";
goto corrupted; 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"; error_msg = "invalid extent entries";
goto corrupted; goto corrupted;
} }
...@@ -508,7 +525,7 @@ static int __ext4_ext_check(const char *function, unsigned int line, ...@@ -508,7 +525,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
} }
#define ext4_ext_check(inode, eh, depth, pblk) \ #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) int ext4_ext_check_inode(struct inode *inode)
{ {
...@@ -541,12 +558,14 @@ static void ext4_cache_extents(struct inode *inode, ...@@ -541,12 +558,14 @@ static void ext4_cache_extents(struct inode *inode,
static struct buffer_head * static struct buffer_head *
__read_extent_tree_block(const char *function, unsigned int line, __read_extent_tree_block(const char *function, unsigned int line,
struct inode *inode, ext4_fsblk_t pblk, int depth, struct inode *inode, struct ext4_extent_idx *idx,
int flags) int depth, int flags)
{ {
struct buffer_head *bh; struct buffer_head *bh;
int err; int err;
ext4_fsblk_t pblk;
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_MOVABLE | GFP_NOFS);
if (unlikely(!bh)) if (unlikely(!bh))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -559,8 +578,8 @@ __read_extent_tree_block(const char *function, unsigned int line, ...@@ -559,8 +578,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
} }
if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE)) if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE))
return bh; return bh;
err = __ext4_ext_check(function, line, inode, err = __ext4_ext_check(function, line, inode, ext_block_hdr(bh),
ext_block_hdr(bh), depth, pblk); depth, pblk, le32_to_cpu(idx->ei_block));
if (err) if (err)
goto errout; goto errout;
set_buffer_verified(bh); set_buffer_verified(bh);
...@@ -578,8 +597,8 @@ __read_extent_tree_block(const char *function, unsigned int line, ...@@ -578,8 +597,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
} }
#define read_extent_tree_block(inode, pblk, depth, flags) \ #define read_extent_tree_block(inode, idx, depth, flags) \
__read_extent_tree_block(__func__, __LINE__, (inode), (pblk), \ __read_extent_tree_block(__func__, __LINE__, (inode), (idx), \
(depth), (flags)) (depth), (flags))
/* /*
...@@ -626,8 +645,7 @@ int ext4_ext_precache(struct inode *inode) ...@@ -626,8 +645,7 @@ int ext4_ext_precache(struct inode *inode)
i--; i--;
continue; continue;
} }
bh = read_extent_tree_block(inode, bh = read_extent_tree_block(inode, path[i].p_idx++,
ext4_idx_pblock(path[i].p_idx++),
depth - i - 1, depth - i - 1,
EXT4_EX_FORCE_CACHE); EXT4_EX_FORCE_CACHE);
if (IS_ERR(bh)) { if (IS_ERR(bh)) {
...@@ -930,8 +948,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block, ...@@ -930,8 +948,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
path[ppos].p_depth = i; path[ppos].p_depth = i;
path[ppos].p_ext = NULL; path[ppos].p_ext = NULL;
bh = read_extent_tree_block(inode, path[ppos].p_block, --i, bh = read_extent_tree_block(inode, path[ppos].p_idx, --i, flags);
flags);
if (IS_ERR(bh)) { if (IS_ERR(bh)) {
ret = PTR_ERR(bh); ret = PTR_ERR(bh);
goto err; goto err;
...@@ -1530,7 +1547,6 @@ static int ext4_ext_search_right(struct inode *inode, ...@@ -1530,7 +1547,6 @@ static int ext4_ext_search_right(struct inode *inode,
struct ext4_extent_header *eh; struct ext4_extent_header *eh;
struct ext4_extent_idx *ix; struct ext4_extent_idx *ix;
struct ext4_extent *ex; struct ext4_extent *ex;
ext4_fsblk_t block;
int depth; /* Note, NOT eh_depth; depth from top of tree */ int depth; /* Note, NOT eh_depth; depth from top of tree */
int ee_len; int ee_len;
...@@ -1597,20 +1613,17 @@ static int ext4_ext_search_right(struct inode *inode, ...@@ -1597,20 +1613,17 @@ static int ext4_ext_search_right(struct inode *inode,
* follow it and find the closest allocated * follow it and find the closest allocated
* block to the right */ * block to the right */
ix++; ix++;
block = ext4_idx_pblock(ix);
while (++depth < path->p_depth) { while (++depth < path->p_depth) {
/* subtract from p_depth to get proper eh_depth */ /* subtract from p_depth to get proper eh_depth */
bh = read_extent_tree_block(inode, block, bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
path->p_depth - depth, 0);
if (IS_ERR(bh)) if (IS_ERR(bh))
return PTR_ERR(bh); return PTR_ERR(bh);
eh = ext_block_hdr(bh); eh = ext_block_hdr(bh);
ix = EXT_FIRST_INDEX(eh); ix = EXT_FIRST_INDEX(eh);
block = ext4_idx_pblock(ix);
put_bh(bh); 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)) if (IS_ERR(bh))
return PTR_ERR(bh); return PTR_ERR(bh);
eh = ext_block_hdr(bh); eh = ext_block_hdr(bh);
...@@ -3041,9 +3054,9 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -3041,9 +3054,9 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
ext_debug("move to level %d (block %llu)\n", ext_debug("move to level %d (block %llu)\n",
i + 1, ext4_idx_pblock(path[i].p_idx)); i + 1, ext4_idx_pblock(path[i].p_idx));
memset(path + i + 1, 0, sizeof(*path)); memset(path + i + 1, 0, sizeof(*path));
bh = read_extent_tree_block(inode, bh = read_extent_tree_block(inode, path[i].p_idx,
ext4_idx_pblock(path[i].p_idx), depth - i - 1, depth - i - 1,
EXT4_EX_NOCACHE); EXT4_EX_NOCACHE);
if (IS_ERR(bh)) { if (IS_ERR(bh)) {
/* should we reset i_size? */ /* should we reset i_size? */
err = PTR_ERR(bh); err = PTR_ERR(bh);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册