aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2017-09-04 20:32:22 -0400
committerTheodore Ts'o <tytso@mit.edu>2017-09-06 10:20:53 -0400
commit32cb8473028406697502ba6698462ba3df909a46 (patch)
treebee8d0c55b0b07da88ce6f5b6d2a627676207cc3
parent8e86ecf704a2c3aa158095451e9e15c61ffc7f11 (diff)
downloade2fsprogs-32cb8473028406697502ba6698462ba3df909a46.tar.gz
e2fsck, libext2fs: add checks for insanely large file systems
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>
-rw-r--r--e2fsck/super.c31
-rw-r--r--lib/ext2fs/openfs.c12
2 files changed, 38 insertions, 5 deletions
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 8153f2bfe..47c89c56f 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -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,
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index da03bc147..f74cd2458 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -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;