ext4: check for out-of-order index extents in ext4_valid_extent_entries()
authorZhang Yi <yi.zhang@huawei.com>
Wed, 8 Sep 2021 12:08:48 +0000 (20:08 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Dec 2021 11:28:37 +0000 (12:28 +0100)
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/extents.c

index e7703b1..01f2b8d 100644 (file)
@@ -370,6 +370,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;
 
@@ -378,31 +381,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;