⬢ DragonFlyBSD Kernel Audit
DF-4829 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/md/md.c
+++ b/sys/dev/disk/md/md.c
@@ -32,6 +32,9 @@
 #define MD_NSECT (10000 * 2)
 #endif
 
+/* Sanity cap for the MD_MALLOC sector pointer array (2^24 sectors = 8 GB). */
+#define MD_MAX_NSECT (1U << 24)
+
 MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
 MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
 
@@ -204,7 +207,8 @@
 	cdev_t dev = ap->a_head.a_dev;
 	struct bio *bio = ap->a_bio;
 	struct buf *bp = bio->bio_buf;
-	unsigned secno, nsec, secval, uc;
+	uint64_t secno;
+	unsigned nsec, secval, uc;
 	u_char *secp, **secpp, *dst;
 	struct md_s *sc;
 	int i;
@@ -250,8 +254,27 @@
 		}
 
 		nsec = bp->b_bcount >> DEV_BSHIFT;
-		secno = (unsigned)(bio->bio_offset >> DEV_BSHIFT);
+		/*
+		 * Keep the full 64-bit sector number.  Truncating to
+		 * unsigned made (secno + nsec + 1) below wrap at 2^32,
+		 * turning the array growth kmalloc() into kmalloc(0) (which
+		 * returns ZERO_LENGTH_PTR) or a wild undersized allocation
+		 * followed by an out-of-bounds bcopy() and a wild write via
+		 * &sc->secp[secno].
+		 */
+		secno = (uint64_t)bio->bio_offset >> DEV_BSHIFT;
 		dst = bp->b_data;
+
+		if (bio->bio_offset < 0 || secno + nsec > MD_MAX_NSECT) {
+			bp->b_error = EIO;
+			bp->b_flags |= B_ERROR;
+			bp->b_resid = bp->b_bcount;
+			devstat_end_transaction_buf(&sc->stats, bp);
+			biodone(bio);
+			crit_enter();
+			continue;
+		}
+
 		while (nsec--) {
 			if (secno < sc->nsecp) {
 				secpp = &sc->secp[secno];
@@ -300,13 +323,14 @@
 						*secpp = (u_char *)(uintptr_t)uc;
 				} else {
 					if (!secpp) {
-						secpp = kmalloc((secno + nsec + 1) * sizeof(u_char *),
+						secpp = kmalloc((size_t)(secno + nsec + 1) *
+								sizeof(u_char *),
 								M_MD,
 								M_WAITOK | M_ZERO);
 						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
 						kfree(sc->secp, M_MD);
 						sc->secp = secpp;
-						sc->nsecp = secno + nsec + 1;
+						sc->nsecp = (unsigned)(secno + nsec + 1);
 						secpp = &sc->secp[secno];
 					}
 					if (i == DEV_BSIZE) {