DragonFlyBSD Kernel Audit
DF-1045 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/cam/scsi/scsi_target.c b/sys/bus/cam/scsi/scsi_target.c
--- a/sys/bus/cam/scsi/scsi_target.c
+++ b/sys/bus/cam/scsi/scsi_target.c
@@ -213,8 +213,8 @@
 	int    error;
 
 	softc = (struct targ_softc *)dev->si_drv1;
-	if ((softc->periph == NULL) ||
-	    (softc->state & TARG_STATE_LUN_ENABLED) == 0) {
+	if (softc->periph == NULL) {
+		/* Never enabled: no periph to tear down; free softc here. */
 		devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor);
 		destroy_dev(dev);
 		kfree(softc, M_TARG);
@@ -223,21 +223,28 @@
 
 	/*
 	 * Acquire a hold on the periph so that it doesn't go away before
-	 * we are ready at the end of the function.
+	 * we are ready at the end of the function.  The final release below
+	 * drops the refcount to 0 with CAM_PERIPH_INVALID set and thus runs
+	 * targdtor() synchronously; targdtor() drains the softc's queues and
+	 * is the sole place softc is kfree()'d, so we must NOT free softc
+	 * before cam_periph_release().
 	 */
 	periph = softc->periph;
 	cam_periph_acquire(periph);
 	cam_periph_lock(periph);
-	error = targdisable(softc);
+	if ((softc->state & TARG_STATE_LUN_ENABLED) != 0)
+		error = targdisable(softc);
+	else
+		error = CAM_REQ_CMP;	/* disabled via TARGIOCDISABLE */
 	if (error == CAM_REQ_CMP) {
 		dev->si_drv1 = 0;
 		if (softc->periph != NULL) {
 			cam_periph_invalidate(softc->periph);
 			softc->periph = NULL;
 		}
-		destroy_dev(dev);	/* eats the open ref */
 		devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor);
-		kfree(softc, M_TARG);
+		destroy_dev(dev);	/* eats the open ref */
+		/* softc freed by targdtor() via cam_periph_release() below */
 	} else {
 		release_dev(dev);
 	}
@@ -549,6 +556,7 @@
 
 	softc->periph = NULL;
 	softc->path = NULL;
+	kfree(softc, M_TARG);
 	periph->softc = NULL;
 }