ext4: fix underflow in ext4_max_bitmap_size()
authorZhang Yi <yi.zhang@huawei.com>
Tue, 1 Mar 2022 11:17:04 +0000 (19:17 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 3 Mar 2022 04:56:41 +0000 (23:56 -0500)
when ext4 filesystem is created with 64k block size, ^extent and
^huge_file features. the upper_limit would underflow during the
computations in ext4_max_bitmap_size(). The problem is the size of block
index tree for such large block size is more than i_blocks can carry.
So fix the computation to count with this possibility. After this fix,
the 'res' cannot overflow loff_t on the extreme case of filesystem with
huge_files and 64K block size, so this patch also revert commit
75ca6ad408f4 ("ext4: fix loff_t overflow in ext4_max_bitmap_size()").

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20220301111704.2153829-1-yi.zhang@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/super.c

index 1fe91a2..1e5f499 100644 (file)
@@ -3481,8 +3481,9 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
  */
 static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
 {
-       unsigned long long upper_limit, res = EXT4_NDIR_BLOCKS;
+       loff_t upper_limit, res = EXT4_NDIR_BLOCKS;
        int meta_blocks;
+       unsigned int ppb = 1 << (bits - 2);
 
        /*
         * This is calculated to be the largest file size for a dense, block
@@ -3514,27 +3515,42 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
 
        }
 
+       /* Compute how many blocks we can address by block tree */
+       res += ppb;
+       res += ppb * ppb;
+       res += ((loff_t)ppb) * ppb * ppb;
+       /* Compute how many metadata blocks are needed */
+       meta_blocks = 1;
+       meta_blocks += 1 + ppb;
+       meta_blocks += 1 + ppb + ppb * ppb;
+       /* Does block tree limit file size? */
+       if (res + meta_blocks <= upper_limit)
+               goto check_lfs;
+
+       res = upper_limit;
+       /* How many metadata blocks are needed for addressing upper_limit? */
+       upper_limit -= EXT4_NDIR_BLOCKS;
        /* indirect blocks */
        meta_blocks = 1;
+       upper_limit -= ppb;
        /* double indirect blocks */
-       meta_blocks += 1 + (1LL << (bits-2));
-       /* tripple indirect blocks */
-       meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2)));
-
-       upper_limit -= meta_blocks;
-       upper_limit <<= bits;
-
-       res += 1LL << (bits-2);
-       res += 1LL << (2*(bits-2));
-       res += 1LL << (3*(bits-2));
+       if (upper_limit < ppb * ppb) {
+               meta_blocks += 1 + DIV_ROUND_UP_ULL(upper_limit, ppb);
+               res -= meta_blocks;
+               goto check_lfs;
+       }
+       meta_blocks += 1 + ppb;
+       upper_limit -= ppb * ppb;
+       /* tripple indirect blocks for the rest */
+       meta_blocks += 1 + DIV_ROUND_UP_ULL(upper_limit, ppb) +
+               DIV_ROUND_UP_ULL(upper_limit, ppb*ppb);
+       res -= meta_blocks;
+check_lfs:
        res <<= bits;
-       if (res > upper_limit)
-               res = upper_limit;
-
        if (res > MAX_LFS_FILESIZE)
                res = MAX_LFS_FILESIZE;
 
-       return (loff_t)res;
+       return res;
 }
 
 static ext4_fsblk_t descriptor_loc(struct super_block *sb,