DragonFlyBSD Kernel Audit
DF-0872 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/ntfs/ntfs_vfsops.c b/sys/vfs/ntfs/ntfs_vfsops.c
--- a/sys/vfs/ntfs/ntfs_vfsops.c
+++ b/sys/vfs/ntfs/ntfs_vfsops.c
@@ -346,12 +346,44 @@
 		goto out;
 	}
 
+	/*
+	 * Validate the BPB-derived fields before dividing/shifting by them.
+	 * A crafted boot sector can otherwise drive a divide-by-zero
+	 * (CPU #DE -> non-resumable kernel trap -> panic) or an undefined
+	 * shift (shift count >= type width).  ntm_bpmftrec is later used as
+	 * a divisor in ntfs_statfs()/ntfs_statvfs(), so it must be non-zero.
+	 */
+	if (ntmp->ntm_bps == 0 || ntmp->ntm_spc == 0) {
+		error = EINVAL;
+		dprintf(("ntfs_mountfs: invalid BPB (bps=%u spc=%u)\n",
+		    ntmp->ntm_bps, ntmp->ntm_spc));
+		goto out;
+	}
 	{
 		int8_t cpr = ntmp->ntm_mftrecsz;
-		if( cpr > 0 )
+		if (cpr > 0) {
 			ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr;
-		else
-			ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps;
+		} else {
+			/*
+			 * cpr <= 0: MFT record size is 2^(-cpr) bytes;
+			 * -cpr is the shift count and must lie in [0,31] to
+			 * stay defined and fit in an int (cpr == INT8_MIN
+			 * would otherwise yield 1 << 128, which is UB).
+			 */
+			int shift = -cpr;
+			if (shift < 0 || shift >= 32) {
+				error = EINVAL;
+				dprintf(("ntfs_mountfs: invalid mftrecsz %d\n",
+				    cpr));
+				goto out;
+			}
+			ntmp->ntm_bpmftrec = (1 << shift) / ntmp->ntm_bps;
+		}
+	}
+	if (ntmp->ntm_bpmftrec == 0) {
+		error = EINVAL;
+		dprintf(("ntfs_mountfs: zero bpmftrec\n"));
+		goto out;
 	}
 	dprintf(("ntfs_mountfs(): bps: %d, spc: %d, media: %x, mftrecsz: %d (%d sects)\n",
 		ntmp->ntm_bps,ntmp->ntm_spc,ntmp->ntm_bootfile.bf_media,