DragonFlyBSD Kernel Audit
DF-1377 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/buslogic/bt.c b/sys/dev/disk/buslogic/bt.c
--- a/sys/dev/disk/buslogic/bt.c
+++ b/sys/dev/disk/buslogic/bt.c
@@ -103,8 +103,20 @@
 static __inline struct bt_ccb *
 btccbptov(struct bt_softc *bt, u_int32_t ccb_addr)
 {
-	return (bt->bt_ccb_array +
-	        ((struct bt_ccb*)(uintptr_t)ccb_addr - (struct bt_ccb*)(uintptr_t)bt->bt_ccb_physbase));
+	struct bt_ccb *bccb;
+	int index;
+
+	bccb = bt->bt_ccb_array +
+	        ((struct bt_ccb*)(uintptr_t)ccb_addr - (struct bt_ccb*)(uintptr_t)bt->bt_ccb_physbase);
+	index = (int)(bccb - bt->bt_ccb_array);
+	/* ccb_addr is a u32 DMA-supplied by the HBA. Without this range
+	 * check, a malicious/buggy HBA can make the computed bccb fall
+	 * outside bt_ccb_array[0..max_ccbs); btdone() then OOB-reads
+	 * bccb->ccb and btfreeccb() OOB-writes bccb->links.sle_next with
+	 * a controlled pointer. */
+	if (index < 0 || index >= bt->max_ccbs)
+		return (NULL);
+	return (bccb);
 }
 
 static __inline u_int32_t
@@ -1362,8 +1374,18 @@
 
 		if ((intstat & IMB_LOADED) != 0) {
 			while (bt->cur_inbox->comp_code != BMBI_FREE) {
+				struct bt_ccb *bccb =
+					btccbptov(bt, bt->cur_inbox->ccb_addr);
+				if (bccb == NULL) {
+					device_printf(bt->dev, "bccb out of "
+					    "range, addr 0x%x, ignoring\n",
+					    bt->cur_inbox->ccb_addr);
+					bt->cur_inbox->comp_code = BMBI_FREE;
+					btnextinbox(bt);
+					continue;
+				}
 				btdone(bt,
-				       btccbptov(bt, bt->cur_inbox->ccb_addr),
+				       bccb,
 				       bt->cur_inbox->comp_code);
 				bt->cur_inbox->comp_code = BMBI_FREE;
 				btnextinbox(bt);