]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck, libext2fs: add checks for insanely large file systems
authorTheodore Ts'o <tytso@mit.edu>
Tue, 5 Sep 2017 00:32:22 +0000 (20:32 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 6 Sep 2017 14:20:53 +0000 (10:20 -0400)
If the blocks count field is too large, this can cause numeric
overflows which can result in buffer overflows.

Addresses-Debian-Bug: #873757

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reported-by: Jakub Wilk <jwilk@jwilk.net>
e2fsck/super.c
lib/ext2fs/openfs.c

index 8153f2bfeda13e13bfbfa05fdfe4487ffa55c259..47c89c56f7face3e3c15af5a443afb41bd217a7c 100644 (file)
@@ -41,6 +41,23 @@ static void check_super_value(e2fsck_t ctx, const char *descr,
        }
 }
 
+static void check_super_value64(e2fsck_t ctx, const char *descr,
+                               __u64 value, int flags,
+                               __u64 min_val, __u64 max_val)
+{
+       struct          problem_context pctx;
+
+       if ((flags & MIN_CHECK && value < min_val) ||
+           (flags & MAX_CHECK && value > max_val) ||
+           (flags & LOG2_CHECK && (value & (value - 1)) != 0)) {
+               clear_problem_context(&pctx);
+               pctx.num = value;
+               pctx.str = descr;
+               fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
+       }
+}
+
 /*
  * helper function to release an inode
  */
@@ -468,6 +485,7 @@ void check_super_block(e2fsck_t ctx)
        problem_t       problem;
        blk64_t blocks_per_group = fs->super->s_blocks_per_group;
        __u32   bpg_max, cpg_max;
+       __u64   blks_max;
        int     inodes_per_block;
        int     inode_size;
        int     accept_time_fudge;
@@ -497,6 +515,15 @@ void check_super_block(e2fsck_t ctx)
        ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
                sizeof(int) * fs->group_desc_count, "invalid_inode_table");
 
+       blks_max = (1ULL << 32) * EXT2_MAX_BLOCKS_PER_GROUP(fs->super);
+       if (ext2fs_has_feature_64bit(fs->super)) {
+               if (blks_max > ((1ULL << 48) - 1))
+                       blks_max = (1ULL << 48) - 1;
+       } else {
+               if (blks_max > ((1ULL << 32) - 1))
+                       blks_max = (1ULL << 32) - 1;
+       }
+
        clear_problem_context(&pctx);
 
        /*
@@ -504,8 +531,8 @@ void check_super_block(e2fsck_t ctx)
         */
        check_super_value(ctx, "inodes_count", sb->s_inodes_count,
                          MIN_CHECK, 1, 0);
-       check_super_value(ctx, "blocks_count", ext2fs_blocks_count(sb),
-                         MIN_CHECK, 1, 0);
+       check_super_value64(ctx, "blocks_count", ext2fs_blocks_count(sb),
+                           MIN_CHECK | MAX_CHECK, 1, blks_max);
        check_super_value(ctx, "first_data_block", sb->s_first_data_block,
                          MAX_CHECK, 0, ext2fs_blocks_count(sb));
        check_super_value(ctx, "log_block_size", sb->s_log_block_size,
index da03bc147de0de3e11487db76df5cebb02488acc..f74cd24585b86a45b06b4a0e18978821fcde3272 100644 (file)
@@ -122,6 +122,7 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
        char            *dest, *cp;
        int             group_zero_adjust = 0;
        int             inode_size;
+       __u64           groups_cnt;
 #ifdef WORDS_BIGENDIAN
        unsigned int    groups_per_block;
        struct ext2_group_desc *gdp;
@@ -371,9 +372,14 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
                retval = EXT2_ET_CORRUPT_SUPERBLOCK;
                goto cleanup;
        }
-       fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) -
-                                                fs->super->s_first_data_block,
-                                                blocks_per_group);
+       groups_cnt = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) -
+                                      fs->super->s_first_data_block,
+                                      blocks_per_group);
+       if (groups_cnt >> 32) {
+               retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+               goto cleanup;
+       }
+       fs->group_desc_count =  groups_cnt;
        if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) !=
            fs->super->s_inodes_count) {
                retval = EXT2_ET_CORRUPT_SUPERBLOCK;