⬢ DragonFlyBSD Kernel Audit
DF-3982 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/sbp/sbp.c
+++ b/sys/dev/disk/sbp/sbp.c
@@ -1597,7 +1597,11 @@
 		ocb  = target->mgm_ocb_cur;
 		if (ocb != NULL) {
 			if (OCB_MATCH(ocb, sbp_status)) {
-				callout_stop(&target->mgm_ocb_timeout);
+				/* DF-3982: recv1 runs in the fwohci hard
+				 * interrupt path under crit; the modern
+				 * callout_stop() would sleep in 'costp'
+				 * while the callback is INPROG. */
+				callout_stop_async(&target->mgm_ocb_timeout);
 				target->mgm_ocb_cur = NULL;
 				break;
 			}
@@ -2129,6 +2133,15 @@
 	struct sbp_dev *sdev = ocb->sdev;
 	struct sbp_target *target = sdev->target;
 
+	crit_enter();
+	if (target->mgm_ocb_cur != ocb) {
+		/* DF-3982: completion raced us (recv1 uses the async stop);
+		 * the ocb has already been completed and freed. */
+		crit_exit();
+		return;
+	}
+	crit_exit();
+
 	sbp_show_sdev_info(sdev, 2);
 	kprintf("request timeout(mgm orb:0x%08x) ... ",
 	    (u_int32_t)ocb->bus_addr);
@@ -2587,7 +2600,8 @@
 			/* found */
 			STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb);
 			if (ocb->ccb != NULL)
-				callout_stop(ocb->ccb->ccb_h.timeout_ch);
+				callout_stop_async(
+					ocb->ccb->ccb_h.timeout_ch);
 			if (ntohl(ocb->orb[4]) & 0xffff) {
 				bus_dmamap_sync(sdev->target->sbp->dmat,
 					ocb->dmamap,
@@ -2702,7 +2716,7 @@
 		bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap);
 	}
 	if (ocb->ccb != NULL) {
-		callout_stop(ocb->ccb->ccb_h.timeout_ch);
+		callout_stop_async(ocb->ccb->ccb_h.timeout_ch);
 		ocb->ccb->ccb_h.status = status;
 		xpt_done(ocb->ccb);
 	}