DF-0803 / fix.diff
diff --git a/sys/vfs/ext2fs/ext2_vfsops.c b/sys/vfs/ext2fs/ext2_vfsops.c --- a/sys/vfs/ext2fs/ext2_vfsops.c +++ b/sys/vfs/ext2fs/ext2_vfsops.c @@ -617,12 +617,24 @@ return (EINVAL); } - fs->e2fs_gcount = howmany(fs->e2fs_bcount - - le32toh(es->e2fs_first_dblock), EXT2_BLOCKS_PER_GROUP(fs)); - if (fs->e2fs_gcount > ((uint64_t)1 << 32) - EXT2_DESCS_PER_BLOCK(fs)) { - SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error, - "groups count too large"); - return (EINVAL); + { + /* + * DF-0803: compute gcount as uint64_t FIRST, validate before + * the narrowing assignment to uint32_t e2fs_gcount. The old + * code truncated via the assignment and then compared the + * already-truncated value against ~2^32, which only caught + * the top 64 uint32 values. + */ + uint64_t gcount64 = howmany(fs->e2fs_bcount - + le32toh(es->e2fs_first_dblock), EXT2_BLOCKS_PER_GROUP(fs)); + + if (gcount64 == 0 || + gcount64 > ((uint64_t)1 << 32) - EXT2_DESCS_PER_BLOCK(fs)) { + SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error, + "groups count too large"); + return (EINVAL); + } + fs->e2fs_gcount = (uint32_t)gcount64; } /* Check for extra isize in big inodes. */ @@ -1349,10 +1361,17 @@ restart: if ((*vpp = ext2_ihashget(ump->um_dev, ino)) != NULL) return (0); + /* + * DF-0803: bounds-check the inode's group against e2fs_gcount before + * any e2fs_gd[ino_to_cg(...)] dereference. Without this, a corrupted + * (or maliciously crafted) image can drive an out-of-bounds GD access. + */ + fs = ump->um_e2fs; + if (ino < EXT2_ROOTINO || ino_to_cg(fs, ino) >= fs->e2fs_gcount) + return (EINVAL); if (ext2_alloc_vnode(mp, ino, &vp) == -1) goto restart; ip = VTOI(vp); - fs = ip->i_e2fs; /* Read in the disk contents for the inode, copy into the inode. */ if ((error = bread(ump->um_devvp, fsbtodoff(fs, ino_to_fsba(fs, ino)), |