DragonFlyBSD Kernel Audit
DF-0797 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c
--- a/sys/vfs/hammer/hammer_ondisk.c
+++ b/sys/vfs/hammer/hammer_ondisk.c
@@ -209,6 +209,22 @@
 	}
 	volume->vol_no = ondisk->vol_no;
 	volume->vol_flags = ondisk->vol_flags;
+
+	/*
+	 * Validate vol_no before it is used as an index into
+	 * hmp->volume_map[] (hammer.h:hammer_volume_number_add).  The
+	 * on-disk vol_no is a raw int32 and is attacker-controlled when a
+	 * crafted filesystem image is mounted; without this check a value
+	 * outside [0, HAMMER_MAX_VOLUMES) drives
+	 * __hammer_vol_index(vol_no)=vol_no>>6 past the 4-element
+	 * volume_map[] array, an out-of-bounds heap write.  (DF-0797)
+	 */
+	if (volume->vol_no < 0 || volume->vol_no >= HAMMER_MAX_VOLUMES) {
+		hkprintf("volume %s has invalid vol_no %d\n",
+			volume->vol_name, volume->vol_no);
+		error = EFTYPE;
+		goto late_failure;
+	}
 	volume->maxbuf_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
 				    HAMMER_VOL_BUF_SIZE(ondisk));
 
diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h
--- a/sys/vfs/hammer/hammer.h
+++ b/sys/vfs/hammer/hammer.h
@@ -1567,7 +1567,14 @@
 static __inline int
 __hammer_vol_index(int vol_no)
 {
-	return(vol_no >> 6);
+	/*
+	 * Mask the index to the size of hammer_mount.volume_map[] (4 qwords,
+	 * HAMMER_MAX_VOLUMES=256 -> 256>>6=4).  vol_no is validated in
+	 * hammer_install_volume() before the first use, but mask here too as
+	 * defense in depth so a stray caller can never index past the array.
+	 * (DF-0797)
+	 */
+	return((vol_no >> 6) & 0x3);
 }
 
 static __inline uint64_t