⬢ DragonFlyBSD Kernel Audit
DF-5377 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/cam/scsi/scsi_target.c
+++ b/sys/bus/cam/scsi/scsi_target.c
@@ -539,6 +539,12 @@
 	 * interested in them.  We free them now.
 	 */
 	while ((ccb_h = TAILQ_FIRST(&softc->user_ccb_queue)) != NULL) {
+		struct targ_cmd_descr *map_descr;
+
+		map_descr = (struct targ_cmd_descr *)ccb_h->targ_descr;
+		if (map_descr->mapinfo.num_bufs_used != 0)
+			cam_periph_unmapmem((union ccb *)ccb_h,
+					    &map_descr->mapinfo);
 		TAILQ_REMOVE(&softc->user_ccb_queue, ccb_h, periph_links.tqe);
 		targfreeccb(softc, (union ccb *)ccb_h);
 	}
@@ -825,11 +831,24 @@
 	TAILQ_REMOVE(&softc->pending_ccb_queue, &done_ccb->ccb_h,
 		     periph_links.tqe);
 
-	/* If we're no longer enabled, throw away CCB */
+
+	/*
+	 * If we're no longer enabled, hand the CCB back to the user
+	 * instead of freeing it here: targdone() runs in the cambio SWI
+	 * context, where neither cam_periph_unmapmem() (copyout) nor
+	 * relpbuf() (the pbuf is locked by the mapping thread) may run.
+	 * Delivering the CCB via user_ccb_queue makes the daemon's
+	 * read()/close path release the mapping, as documented in
+	 * scsi_targetio.h ("aborted CCBs will be returned to the user
+	 * via read(2) or discarded if the user closes the device").
+	 */
 	if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) {
-		targfreeccb(softc, done_ccb);
+		TAILQ_INSERT_TAIL(&softc->user_ccb_queue, &done_ccb->ccb_h,
+				  periph_links.tqe);
+		notify_user(softc);
 		return;
 	}
+
 	/* abort_all_pending() waits for pending queue to be empty */
 	if (TAILQ_EMPTY(&softc->pending_ccb_queue))
 		wakeup(&softc->pending_ccb_queue);