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 @@ -644,6 +644,27 @@ error = EINVAL; /* XXX needs translation */ goto out; } + /* + * Validate the block/frag geometry used by the blkoff()/fragoff()/ + * blkroundup() macros (ufs/fs.h:487-517). blkoff(fs, loc) is + * `loc & fs_qbmask' and must yield a value in [0, fs_bsize). This + * holds iff fs_qbmask == fs_bsize - 1 (and fs_bsize is a power of 2, + * which the power-of-two check below enforces). fs_qbmask is loaded + * verbatim from disk for modern filesystems (ffs_oldfscompat only + * re-derives it when fs_inodefmt < FS_44INODEFMT; newfs sets =2), so + * a crafted fs_qbmask larger than fs_bsize-1 makes blkoff() exceed + * fs_bsize and drives xfersize = fs_bsize - blkoffset negative in + * ffs_write (ufs_readwrite.c:293), panicking the kernel in ffs_balloc. + * Reject the mount instead of trusting on-disk mask values. + */ + if (fs->fs_fsize <= 0 || fs->fs_fsize > fs->fs_bsize || + (fs->fs_bsize & (fs->fs_bsize - 1)) != 0 || + (fs->fs_fsize & (fs->fs_fsize - 1)) != 0 || + fs->fs_qbmask != (int64_t)(fs->fs_bsize - 1) || + fs->fs_qfmask != (int64_t)(fs->fs_fsize - 1)) { + error = EINVAL; /* malformed superblock geometry */ + goto out; + } fs->fs_fmod = 0; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) {