⬢ DragonFlyBSD Kernel Audit
DF-4307 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/advansys/adwcam.c
+++ b/sys/dev/disk/advansys/adwcam.c
@@ -1265,6 +1265,8 @@
 		struct adw_carrier *free_carrier;
 		struct acb *acb;
 		union ccb *ccb;
+		u_int32_t areq_ba;
+		u_int32_t next_ba;
 
 #if 0
 		kprintf("0x%x, 0x%x, 0x%x, 0x%x\n",
@@ -1276,8 +1278,23 @@
 		/*
 		 * The firmware copies the adw_scsi_req_q.acb_baddr
 		 * field into the areq_ba field of the carrier.
+		 *
+		 * DF-4307: areq_ba/next_ba are device-written 32-bit
+		 * tokens.  acbbotov()/carrierbotov() do no bounds
+		 * checking, so a bogus token yields an out-of-bounds
+		 * kernel pointer that is read from AND written through
+		 * below.  Validate both against the allocations before
+		 * converting them to pointers.
 		 */
-		acb = acbbotov(adw, adw->responseq->areq_ba);
+		areq_ba = adw->responseq->areq_ba;
+		if (areq_ba >= adw->num_acbs * sizeof(struct acb) ||
+		    (areq_ba % sizeof(struct acb)) != 0) {
+			kprintf("%s: bogus completion token 0x%x\n",
+				adw_name(adw), areq_ba);
+			adw_reset_bus(adw);
+			break;
+		}
+		acb = acbbotov(adw, areq_ba);
 
 		/*
 		 * The least significant four bits of the next_ba
@@ -1285,8 +1302,15 @@
 		 * advance through the list.
 		 */
 		free_carrier = adw->responseq;
-		adw->responseq =
-		    carrierbotov(adw, free_carrier->next_ba & ADW_NEXT_BA_MASK);
+		next_ba = free_carrier->next_ba & ADW_NEXT_BA_MASK;
+		if (next_ba >= (adw->max_acbs + ADW_NUM_CARRIER_QUEUES + 1)
+			     * sizeof(struct adw_carrier)) {
+			kprintf("%s: bogus carrier link 0x%x\n",
+				adw_name(adw), next_ba);
+			adw_reset_bus(adw);
+			break;
+		}
+		adw->responseq = carrierbotov(adw, next_ba);
 		free_carrier->next_ba = adw->free_carriers->carr_offset;
 		adw->free_carriers = free_carrier;