DragonFlyBSD Kernel Audit
DF-0869 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer/hammer_volume.c b/sys/vfs/hammer/hammer_volume.c
--- a/sys/vfs/hammer/hammer_volume.c
+++ b/sys/vfs/hammer/hammer_volume.c
@@ -647,6 +647,21 @@
 	ondisk->vol_count = root_ondisk->vol_count + 1;
 
 	/*
+	 * Validate attacker-controlled geometry from the ioctl before letting
+	 * it shape the on-disk volume header.  boot_area_size, memory_log_size
+	 * and vol_size are int64 fields supplied by userspace and previously
+	 * only checked via a single signed-<0 test on the resulting
+	 * vol_buf_size; with e.g. boot_area_size = -1 the un-aligned
+	 * vol_buf_beg corrupts subsequent freemap I/O and panics the kernel.
+	 */
+	if (ioc->boot_area_size < 0 || ioc->memory_log_size < 0 ||
+	    ioc->vol_size <= 0) {
+		hmkprintf(hmp, "volume %d has non-positive geometry\n",
+			vol_no);
+		return(EFTYPE);
+	}
+
+	/*
 	 * Reserve space for (future) header junk.
 	 */
 	vol_alloc = root_ondisk->vol_bot_beg;
@@ -667,6 +682,24 @@
 		return(EFTYPE);
 	}
 
+	/*
+	 * The buffer area must fit in HAMMER_OFF_SHORT_MASK (the lower 52
+	 * bits of a hammer_off_t) and vol_buf_beg must be HAMMER_BUFSIZE
+	 * aligned, otherwise hammer_format_freemap's KKASSERT trips and /
+	 * or its hammer_bread() I/O targets a non-sector-aligned offset
+	 * (both observed as a kernel panic).  HAMMER_OFF_SHORT_MASK is
+	 * uint64; cast through uint64_t to avoid signed-comparison noise.
+	 */
+	if ((uint64_t)HAMMER_VOL_BUF_SIZE(ondisk) > HAMMER_OFF_SHORT_MASK ||
+	    (ondisk->vol_buf_beg & HAMMER_BUFMASK64) != 0) {
+		hmkprintf(hmp, "volume %d has insane geometry: "
+			"vol_buf_beg=%jd vol_buf_end=%jd\n",
+			ondisk->vol_no,
+			(intmax_t)ondisk->vol_buf_beg,
+			(intmax_t)ondisk->vol_buf_end);
+		return(EFTYPE);
+	}
+
 	return(0);
 }