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)

Reply via email to