From: Ana Bobillo <[email protected]> ext4fs_init() divides by s_blocks_per_group from the superblock to count the block groups, so a value of zero crashes the write path at once. The bitmap code of the write path also assumes one bitmap block's worth of blocks per group: ext4fs_set_block_bmap() and ext4fs_reset_block_bmap() find the bits of group n at n * blocksize bytes into its bitmap, and ext4fs_get_new_blk_no() numbers the blocks of group n from n * blocksize * 8. With any other s_blocks_per_group the groups after the first are indexed outside their bitmap buffers. s_inodes_per_group likewise cannot exceed the bits of one bitmap block.
mke2fs uses that layout unless it is told otherwise; it lowers s_blocks_per_group for 64 KiB blocks, with -g, or when many inodes are asked for. Check the layout at the start of ext4fs_init(), before the journal is replayed, and refuse to write to a filesystem of more than one group laid out any other way, instead of dividing by zero or indexing outside the bitmap buffers. A filesystem of a single group is still written to, as before. Signed-off-by: Ana Bobillo <[email protected]> Reviewed-by: Simon Glass <[email protected]> --- Changes in v2: - Use printf() rather than log_err(), as the rest of the driver does fs/ext4/ext4_write.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c index 2fba2197c19..5f185387030 100644 --- a/fs/ext4/ext4_write.c +++ b/fs/ext4/ext4_write.c @@ -605,11 +605,30 @@ int ext4fs_init(void) int i; uint32_t real_free_blocks = 0; struct ext_filesystem *fs = get_fs(); + struct ext2_sblock *sblock = &ext4fs_root->sblock; + uint32_t blk_per_grp = le32_to_cpu(sblock->blocks_per_group); + uint32_t inode_per_grp = le32_to_cpu(sblock->inodes_per_group); /* populate fs */ fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root); fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz; + /* + * The bitmap code finds the bitmap bits of group n at n * blocksize + * bytes, so every group must hold one bitmap block's worth of + * blocks, unless there is only one group. Check this before the + * journal is replayed, so that nothing is written to a filesystem + * that is refused. + */ + if (!blk_per_grp || blk_per_grp > fs->blksz * 8 || + (blk_per_grp != fs->blksz * 8 && + le32_to_cpu(sblock->total_blocks) - + le32_to_cpu(sblock->first_data_block) > blk_per_grp) || + !inode_per_grp || inode_per_grp > fs->blksz * 8) { + printf("Unsupported block group layout, not writing\n"); + return -EINVAL; + } + /* get the superblock */ fs->sb = zalloc(SUPERBLOCK_SIZE); if (!fs->sb)
