libext2fs: Add checks to prevent integer overflows passed to malloc()
This addresses a potential security vulnerability where an untrusted
filesystem can be corrupted in such a way that a program using
libext2fs will allocate a buffer which is far too small. This can
lead to either a crash or potentially a heap-based buffer overflow
crash. No known exploits exist, but main concern is where an
untrusted user who possesses privileged access in a guest Xen
environment could corrupt a filesystem which is then accessed by the
pygrub program, running as root in the dom0 host environment, thus
allowing the untrusted user to gain privileged access in the host OS.
Thanks to the McAfee AVERT Research group for reporting this issue.
Addresses CVE-2007-5497.
Signed-off-by: Rafal Wojtczuk <rafal_wojtczuk@mcafee.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
index 50e6336..da7889d 100644
--- a/lib/ext2fs/badblocks.c
+++ b/lib/ext2fs/badblocks.c
@@ -42,7 +42,7 @@
bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST;
bb->size = size ? size : 10;
bb->num = num;
- retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list);
+ retval = ext2fs_get_array(bb->size, sizeof(blk_t), &bb->list);
if (retval) {
ext2fs_free_mem(&bb);
return retval;
diff --git a/lib/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c
index 97a5608..1f5b4e8 100644
--- a/lib/ext2fs/bb_inode.c
+++ b/lib/ext2fs/bb_inode.c
@@ -68,7 +68,7 @@
rec.bad_block_count = 0;
rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
rec.max_ind_blocks = 10;
- retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
+ retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t),
&rec.ind_blocks);
if (retval)
return retval;
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index 7685680..07a6415 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -313,7 +313,7 @@
if (block_buf) {
ctx.ind_buf = block_buf;
} else {
- retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
+ retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf);
if (retval)
return retval;
}
diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index e840044..754fc49 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -158,7 +158,7 @@
addr_per_block = (blk_t) fs->blocksize >> 2;
if (!block_buf) {
- retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
+ retval = ext2fs_get_array(2, fs->blocksize, &buf);
if (retval)
return retval;
block_buf = buf;
diff --git a/lib/ext2fs/bmove.c b/lib/ext2fs/bmove.c
index 9946c46..7d4b21a 100644
--- a/lib/ext2fs/bmove.c
+++ b/lib/ext2fs/bmove.c
@@ -108,7 +108,7 @@
pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
pb.flags = flags;
- retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf);
+ retval = ext2fs_get_array(4, fs->blocksize, &block_buf);
if (retval)
return retval;
pb.buf = block_buf + fs->blocksize * 3;
diff --git a/lib/ext2fs/brel_ma.c b/lib/ext2fs/brel_ma.c
index d422bb2..3753e9f 100644
--- a/lib/ext2fs/brel_ma.c
+++ b/lib/ext2fs/brel_ma.c
@@ -75,7 +75,8 @@
size = (size_t) (sizeof(struct ext2_block_relocate_entry) *
(max_block+1));
- retval = ext2fs_get_mem(size, &ma->entries);
+ retval = ext2fs_get_array(max_block+1,
+ sizeof(struct ext2_block_relocate_entry), &ma->entries);
if (retval)
goto errout;
memset(ma->entries, 0, size);
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 474e00e..f3fe533 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -226,8 +226,7 @@
retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
if (retval)
goto errout;
- retval = ext2fs_get_mem((size_t)(fs->blocksize *
- fs->desc_blocks),
+ retval = ext2fs_get_array(fs->blocksize, fs->desc_blocks,
&group_shadow);
if (retval)
goto errout;
diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index d5833d7..21b36aa 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -85,7 +85,8 @@
}
len = (size_t) sizeof(struct ext2_db_entry) * dblist->size;
dblist->count = count;
- retval = ext2fs_get_mem(len, &dblist->list);
+ retval = ext2fs_get_array(dblist->size, sizeof(struct ext2_db_entry),
+ &dblist->list);
if (retval)
goto cleanup;
diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
index f8919c2..1cf314c 100644
--- a/lib/ext2fs/dupfs.c
+++ b/lib/ext2fs/dupfs.c
@@ -59,7 +59,7 @@
goto errout;
memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE);
- retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+ retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize,
&fs->group_desc);
if (retval)
goto errout;
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 7645210..06ba9a2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -965,6 +965,7 @@
/* inline functions */
extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
+extern errcode_t ext2fs_get_array(unsigned long count, unsigned long size, void *ptr);
extern errcode_t ext2fs_free_mem(void *ptr);
extern errcode_t ext2fs_resize_mem(unsigned long old_size,
unsigned long size, void *ptr);
@@ -1018,6 +1019,12 @@
memcpy(ptr, &pp, sizeof (pp));
return 0;
}
+_INLINE_ errcode_t ext2fs_get_array(unsigned long count, unsigned long size, void *ptr)
+{
+ if (count && (-1UL)/count<size)
+ return EXT2_ET_NO_MEMORY; //maybe define EXT2_ET_OVERFLOW ?
+ return ext2fs_get_mem(count*size, ptr);
+}
/*
* Free memory
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index 3e42cbc..8bf99fb 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -65,7 +65,7 @@
goto fail;
}
- retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
+ retval = ext2fs_get_array(3, fs->blocksize, &file->buf);
if (retval)
goto fail;
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
index de0b614..2905676 100644
--- a/lib/ext2fs/icount.c
+++ b/lib/ext2fs/icount.c
@@ -237,7 +237,8 @@
printf("Icount allocated %u entries, %d bytes.\n",
icount->size, bytes);
#endif
- retval = ext2fs_get_mem(bytes, &icount->list);
+ retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
+ &icount->list);
if (retval)
goto errout;
memset(icount->list, 0, bytes);
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index 16e9eaa..c8f48b9 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -351,7 +351,7 @@
ext2fs_free_mem(&buf);
- retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+ retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize,
&fs->group_desc);
if (retval)
goto cleanup;
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 49d012c..7a0cf4e 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -90,9 +90,9 @@
fs->icache->cache_last = -1;
fs->icache->cache_size = 4;
fs->icache->refcount = 1;
- retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
- * fs->icache->cache_size,
- &fs->icache->cache);
+ retval = ext2fs_get_array(fs->icache->cache_size,
+ sizeof(struct ext2_inode_cache_ent),
+ &fs->icache->cache);
if (retval) {
ext2fs_free_mem(&fs->icache->buffer);
ext2fs_free_mem(&fs->icache);
@@ -146,8 +146,8 @@
group_desc[scan->current_group].bg_inode_table;
scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
scan->blocks_left = scan->fs->inode_blocks_per_group;
- retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
- fs->blocksize),
+ retval = ext2fs_get_array(scan->inode_buffer_blocks,
+ fs->blocksize,
&scan->inode_buffer);
scan->done_group = 0;
scan->done_group_data = 0;
diff --git a/lib/ext2fs/irel_ma.c b/lib/ext2fs/irel_ma.c
index eedbe55..c6fa828 100644
--- a/lib/ext2fs/irel_ma.c
+++ b/lib/ext2fs/irel_ma.c
@@ -90,21 +90,24 @@
irel->priv_data = ma;
size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1));
- retval = ext2fs_get_mem(size, &ma->orig_map);
+ retval = ext2fs_get_array(max_inode+1, sizeof(ext2_ino_t),
+ &ma->orig_map);
if (retval)
goto errout;
memset(ma->orig_map, 0, size);
size = (size_t) (sizeof(struct ext2_inode_relocate_entry) *
(max_inode+1));
- retval = ext2fs_get_mem(size, &ma->entries);
+ retval = ext2fs_get_array((max_inode+1,
+ sizeof(struct ext2_inode_relocate_entry), &ma->entries);
if (retval)
goto errout;
memset(ma->entries, 0, size);
size = (size_t) (sizeof(struct inode_reference_entry) *
(max_inode+1));
- retval = ext2fs_get_mem(size, &ma->ref_entries);
+ retval = ext2fs_get_mem(max_inode+1,
+ sizeof(struct inode_reference_entry), &ma->ref_entries);
if (retval)
goto errout;
memset(ma->ref_entries, 0, size);
@@ -249,7 +252,8 @@
if (ref_ent->refs == 0) {
size = (size_t) ((sizeof(struct ext2_inode_reference) *
ent->max_refs));
- retval = ext2fs_get_mem(size, &ref_ent->refs);
+ retval = ext2fs_get_array(ent->max_refs,
+ sizeof(struct ext2_inode_reference), &ref_ent->refs);
if (retval)
return retval;
memset(ref_ent->refs, 0, size);
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 1ac1564..947675e 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -280,7 +280,7 @@
blocks_per_group);
fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
EXT2_DESC_PER_BLOCK(fs->super));
- retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize,
+ retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize,
&fs->group_desc);
if (retval)
goto cleanup;
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index 9901c6e..fa72fc9 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -73,7 +73,7 @@
sb = fs->super;
- retval = ext2fs_get_mem(2 * fs->blocksize, &dindir_buf);
+ retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf);
if (retval)
goto out_free;
gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);