DragonFlyBSD Kernel Audit
DF-1070 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/cam/scsi/scsi_targ_bh.c b/sys/bus/cam/scsi/scsi_targ_bh.c
--- a/sys/bus/cam/scsi/scsi_targ_bh.c
+++ b/sys/bus/cam/scsi/scsi_targ_bh.c
@@ -85,6 +85,7 @@
 	struct		ccb_queue pending_queue;
 	struct		ccb_queue work_queue;
 	struct		ccb_queue unknown_atio_queue;
+	u_int		outstanding_ctios;
 	struct		devstat device_stats;
 	targbh_state	state;
 	targbh_flags	flags;	
@@ -416,8 +417,16 @@
 	case 1:
 		/* FALLTHROUGH */
 	default:
-		/* XXX Wait for callback of targbhdislun() */
-		sim_lock_sleep(softc, 0, "targbh", hz/2, periph->sim->lock);
+		/*
+		 * Wait for all in-flight CTIOs to complete.  targbhdone()
+		 * decrements outstanding_ctios and wakes us when it hits 0.
+		 * Replaces the fixed 500 ms timeout that had no wakeup
+		 * source and could kfree(softc) while a CTIO completion
+		 * was still outstanding -> use-after-free in targbhdone().
+		 */
+		while (softc->outstanding_ctios > 0)
+			sim_lock_sleep(&softc->outstanding_ctios, 0, "targbh",
+				       hz, periph->sim->lock);
 		kfree(softc, M_SCSIBH);
 		break;
 	}
@@ -492,6 +501,8 @@
 
 		start_ccb->ccb_h.ccb_type = TARGBH_CCB_WORKQ;
 		start_ccb->ccb_h.ccb_atio = atio;
+		/* Track in-flight CTIO so targbhdtor() can wait for it. */
+		softc->outstanding_ctios++;
 		CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
 			  ("Sending a CTIO\n"));
 		xpt_action(start_ccb);
@@ -675,6 +686,12 @@
 		}
 		desc->data_resid -= desc->data_increment;
 		xpt_release_ccb(done_ccb);
+		KKASSERT(softc->outstanding_ctios > 0);
+		if (--softc->outstanding_ctios == 0) {
+			/* Wake targbhdtor() teardown wait. */
+			wakeup_one(&softc->outstanding_ctios);
+		}
+
 		if (softc->state != TARGBH_STATE_TEARDOWN) {
 
 			/*