⬢ DragonFlyBSD Kernel Audit
DF-3190 / fix.diff
← back to finding ↓ download raw
--- 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) {