⬢ DragonFlyBSD Kernel Audit
DF-4350 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/advansys/advansys.c
+++ b/sys/dev/disk/advansys/advansys.c
@@ -1036,16 +1036,31 @@
 	struct adv_q_done_info scsiq;
 	u_int		  doneq_head;
 	u_int		  done_qno;
+	int		  doneq_budget;
 
 	doneq_head = adv_read_lram_16(adv, ADVV_DONE_Q_TAIL_W) & 0xFF;
 	done_qno = adv_read_lram_8(adv, ADV_QNO_TO_QADDR(doneq_head)
 				   + ADV_SCSIQ_B_FWD);
+	doneq_budget = ADV_MAX_TOTAL_QNG + ADV_MAX_SG_LIST;
 	while (done_qno != ADV_QLINK_END) {
 		union ccb* ccb;
 		struct adv_ccb_info *cinfo;
 		u_int done_qaddr;
 		u_int sg_queue_cnt;
 
+		/*
+		 * DF-4350: loop termination depends entirely on the
+		 * device-written FWD bytes in LRAM; a corrupt or hostile
+		 * cyclic chain used to spin adv_intr() forever in
+		 * interrupt context.  A legitimate done queue can never
+		 * be longer than every queue entry plus every SG link.
+		 */
+		if (doneq_budget-- <= 0) {
+			kprintf("%s: done queue walk exceeded %d entries, "
+				"stopping (corrupt LRAM?)\n", adv_name(adv),
+				ADV_MAX_TOTAL_QNG + ADV_MAX_SG_LIST);
+			break;
+		}
 		done_qaddr = ADV_QNO_TO_QADDR(done_qno);
 
 		/* Pull status from this request */