diff --git a/sys/vfs/ufs/ffs_vfsops.c b/sys/vfs/ufs/ffs_vfsops.c --- a/sys/vfs/ufs/ffs_vfsops.c +++ b/sys/vfs/ufs/ffs_vfsops.c @@ -486,6 +486,20 @@ brelse(bp); return (EIO); /* XXX needs translation */ } + /* + * Validate fs_frag for the same reasons as ffs_mountfs(): it indexes + * fixed-size tables and discriminates the bitmap helpers in ffs_subr.c. + */ + switch ((int)newfs->fs_frag) { + case 1: + case 2: + case 4: + case 8: + break; + default: + brelse(bp); + return (EIO); + } fs = VFSTOUFS(mp)->um_fs; /* * Copy pointer fields back into superblock before copying in XXX @@ -644,6 +658,23 @@ error = EINVAL; /* XXX needs translation */ goto out; } + /* + * fs_frag indexes fixed-size lookup tables (fragtbl[], around[], + * inside[], cg_frsum[]) and is the switch discriminator of the + * block-bitmap helpers in ffs_subr.c. Any value outside {1,2,4,8} + * would panic the kernel on the first block alloc/free (DoS) and, + * for fs_frag > MAXFRAG, drives out-of-bounds writes in ffs_fragacct(). + */ + switch ((int)fs->fs_frag) { + case 1: + case 2: + case 4: + case 8: + break; + default: + error = EINVAL; + goto out; + } fs->fs_fmod = 0; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { diff --git a/sys/vfs/ufs/ffs_subr.c b/sys/vfs/ufs/ffs_subr.c --- a/sys/vfs/ufs/ffs_subr.c +++ b/sys/vfs/ufs/ffs_subr.c @@ -167,6 +167,15 @@ int siz, pos; /* + * Defense-in-depth: fragtbl[], around[], inside[], and cg_frsum[] + * are sized for fs_frag in [1, MAXFRAG]. ffs_mountfs() should have + * rejected anything else, but bail out here too so a future caller + * or a missed path cannot drive an out-of-bounds table access. + */ + if (fs->fs_frag <= 0 || fs->fs_frag > MAXFRAG) + return; + + /* * inblk represents a bitmap of fragment sizes which may be * contained in the data 'fragmap'. e.g. if a fragment of size * 1 is available, bit 0 would be set. inblk is shifted left