DragonFlyBSD Kernel Audit
DF-0820 / fix.diff
← back to finding ↓ download raw
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
@@ -593,7 +593,8 @@
 	struct fs *fs;
 	cdev_t dev;
 	void *space;
-	int error, i, blks, size, ronly;
+	int error, i, blks, ronly;
+	size_t size;
 	int32_t *lp;
 	uint64_t maxfilesize;					/* XXX */
 
@@ -644,6 +645,27 @@
 		error = EINVAL;		/* XXX needs translation */
 		goto out;
 	}
+	/*
+	 * DF-0820: validate superblock geometry before any of these fields is
+	 * used as a divisor, an allocation size, or a loop bound.  A crafted
+	 * image could otherwise cause: divide-by-zero (fs_fsize/fs_frag/fs_ipg/
+	 * fs_fpg == 0), an unbounded bzero from a size_t-wrapped `size'
+	 * (fs_ncg < 0 -> bzero(fs_contigdirs, (size_t)-1)), a controlled-value
+	 * heap overflow from the maxcluster init loop (fs_ncg large enough that
+	 * the `int size' allocation wrapped small), an out-of-bounds read in the
+	 * superblock bcopy (fs_sbsize > SBSIZE), or reads past the kmalloc'd
+	 * superblock copy (fs_sbsize < sizeof(struct fs)).
+	 */
+	if (fs->fs_sbsize < (int32_t)sizeof(struct fs) ||
+	    fs->fs_sbsize > SBSIZE ||
+	    fs->fs_fsize <= 0 || fs->fs_fsize > fs->fs_bsize ||
+	    fs->fs_frag <= 0 || (fs->fs_frag - 1) & fs->fs_frag ||
+	    fs->fs_ncg <= 0 || fs->fs_ncg > 1000000 ||
+	    fs->fs_ipg <= 0 || fs->fs_fpg <= 0 ||
+	    fs->fs_cssize < 0 || fs->fs_cssize > (1 << 30)) {
+		error = EINVAL;
+		goto out;
+	}
 	fs->fs_fmod = 0;
 	fs->fs_flags &= ~FS_UNCLEAN;
 	if (fs->fs_clean == 0) {