ext4: check for overlapping extents in ext4_valid_extent_entries()
A corrupted ext4 may have out of order leaf extents, i.e. extent: lblk 0--1023, len 1024, pblk 9217, flags: LEAF UNINIT extent: lblk 1000--2047, len 1024, pblk 10241, flags: LEAF UNINIT ^^^^ overlap with previous extent Reading such extent could hit BUG_ON() in ext4_es_cache_extent(). BUG_ON(end < lblk); The problem is that __read_extent_tree_block() tries to cache holes as well but assumes 'lblk' is greater than 'prev' and passes underflowed length to ext4_es_cache_extent(). Fix it by checking for overlapping extents in ext4_valid_extent_entries(). I hit this when fuzz testing ext4, and am able to reproduce it by modifying the on-disk extent by hand. Also add the check for (ee_block + len - 1) in ext4_valid_extent() to make sure the value is not overflow. Ran xfstests on patched ext4 and no regression. Cc: Lukáš Czerner <lczerner@redhat.com> Signed-off-by: Eryu Guan <guaneryu@gmail.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: stable@vger.kernel.org
This commit is contained in:
Родитель
4e8d213980
Коммит
5946d08937
|
@ -360,8 +360,10 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
|
||||||
{
|
{
|
||||||
ext4_fsblk_t block = ext4_ext_pblock(ext);
|
ext4_fsblk_t block = ext4_ext_pblock(ext);
|
||||||
int len = ext4_ext_get_actual_len(ext);
|
int len = ext4_ext_get_actual_len(ext);
|
||||||
|
ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
|
||||||
|
ext4_lblk_t last = lblock + len - 1;
|
||||||
|
|
||||||
if (len == 0)
|
if (lblock > last)
|
||||||
return 0;
|
return 0;
|
||||||
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
|
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
|
||||||
}
|
}
|
||||||
|
@ -387,11 +389,26 @@ 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);
|
||||||
|
struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
|
||||||
|
ext4_fsblk_t pblock = 0;
|
||||||
|
ext4_lblk_t lblock = 0;
|
||||||
|
ext4_lblk_t prev = 0;
|
||||||
|
int len = 0;
|
||||||
while (entries) {
|
while (entries) {
|
||||||
if (!ext4_valid_extent(inode, ext))
|
if (!ext4_valid_extent(inode, ext))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Check for overlapping extents */
|
||||||
|
lblock = le32_to_cpu(ext->ee_block);
|
||||||
|
len = ext4_ext_get_actual_len(ext);
|
||||||
|
if ((lblock <= prev) && prev) {
|
||||||
|
pblock = ext4_ext_pblock(ext);
|
||||||
|
es->s_last_error_block = cpu_to_le64(pblock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
ext++;
|
ext++;
|
||||||
entries--;
|
entries--;
|
||||||
|
prev = lblock + len - 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
|
struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче