⬢ DragonFlyBSD Kernel Audit
DF-4349 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/advansys/advansys.c
+++ b/sys/dev/disk/advansys/advansys.c
@@ -1089,6 +1089,27 @@
 #endif		
 		adv->cur_active -= sg_queue_cnt + 1;
 
+		/*
+		 * DF-4349: ccb_index is a raw 32-bit value written by the
+		 * RISC into LRAM.  Never-allocated slots (the array is
+		 * kmalloc'd without M_ZERO) and slots that were already
+		 * completed (ACCB_FREE, stale cinfo->ccb) used to be
+		 * completed a second time: double xpt_done, LIST_REMOVE of
+		 * a detached CCB, callout_stop/dmamap_unload on recycled
+		 * resources.  Only an ACCB_ACTIVE ccb_info may complete.
+		 */
+		if (scsiq.d2.ccb_index >= adv->ccb_infos_allocated
+		 || (adv->ccb_infos[scsiq.d2.ccb_index].state
+		     & ACCB_ACTIVE) == 0) {
+			kprintf("%s: done queue ccb_index %d not active, "
+				"dropping completion\n", adv_name(adv),
+				scsiq.d2.ccb_index);
+			doneq_head = done_qno;
+			done_qno = adv_read_lram_8(adv, done_qaddr
+						   + ADV_SCSIQ_B_FWD);
+			continue;
+		}
+
 		if ((scsiq.q_status != QS_DONE)
 		 && (scsiq.q_status & QS_ABORTED) == 0)
 			panic("adv_qdone: completed scsiq with unknown status");
@@ -1294,7 +1315,7 @@
 	 * upper level SCSI transaction it represents.
 	 */
 	adv->ccb_infos = kmalloc(sizeof(*adv->ccb_infos) * adv->max_openings,
-				M_DEVBUF, M_WAITOK);
+				M_DEVBUF, M_WAITOK | M_ZERO);
 	adv->init_level++;
 		
 	/*