--- a/sys/vfs/ufs/ffs_vfsops.c +++ b/sys/vfs/ufs/ffs_vfsops.c @@ -582,6 +582,19 @@ } /* + * DF-3190: log2 of a power-of-two geometry constant (bsize/fsize/frag). + */ +static int +ffs_geomshift(int v) +{ + int r; + + for (r = 0; (1 << (r + 1)) <= v; r++) + ; + return (r); +} + +/* * Common code for mount and mountroot */ int @@ -644,6 +657,33 @@ error = EINVAL; /* XXX needs translation */ goto out; } + /* + * The derived fragment-geometry fields feed kernel-side buffer + * sizing (fragroundup()/blkoff() in ffs_balloc()) and fragment + * arithmetic (numfrags()/fragoff()/ffs_fragextend()). They are + * computed by newfs(8) from fs_bsize/fs_fsize but were never + * validated, so a crafted superblock could make fragroundup() + * round DOWN and hand ffs_write() a buffer smaller than the + * uiomove() that follows it (DF-3190). Require the on-disk + * copies to match the values recomputed from fs_bsize/fs_fsize. + */ + if (!powerof2(fs->fs_bsize) || + fs->fs_fsize < DEV_BSIZE || fs->fs_fsize > fs->fs_bsize || + !powerof2(fs->fs_fsize) || (fs->fs_bsize % fs->fs_fsize) != 0 || + fs->fs_frag != fs->fs_bsize / fs->fs_fsize || + fs->fs_frag < 1 || fs->fs_frag > MAXFRAG || + !powerof2(fs->fs_frag) || + fs->fs_bmask != ~(fs->fs_bsize - 1) || + fs->fs_fmask != ~(fs->fs_fsize - 1) || + fs->fs_qbmask != (int64_t)(fs->fs_bsize - 1) || + fs->fs_qfmask != (int64_t)(fs->fs_fsize - 1) || + fs->fs_bshift != ffs_geomshift(fs->fs_bsize) || + fs->fs_fshift != ffs_geomshift(fs->fs_fsize) || + fs->fs_fragshift != ffs_geomshift(fs->fs_frag)) { + kprintf("ffs_mountfs: inconsistent fragment geometry\n"); + error = EINVAL; + goto out; + } fs->fs_fmod = 0; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) {