⬢ DragonFlyBSD Kernel Audit
DF-3607 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/ncr/ncr.c
+++ b/sys/dev/disk/ncr/ncr.c
@@ -5165,6 +5165,18 @@ ncr_timeout (void *arg)
 			if (cp->phys.header.launch.l_paddr ==
 				NCB_SCRIPT_PHYS (np, select)) {
 				kprintf ("%s: timeout nccb=%p (skip)\n",
 					ncr_name (np), cp);
 				cp->phys.header.launch.l_paddr
 				= NCB_SCRIPT_PHYS (np, skip);
 			}
+
+			/*
+			**	DF-3607: If the script processor is still
+			**	connected with this nccb (DSA points at it),
+			**	completing it here would leave the chip armed
+			**	with a dead data structure.  Recover the hard
+			**	way: reset the bus so ncr_init() invalidates
+			**	the chip state BEFORE completing the jobs.
+			*/
+			if (INL (nc_dsa) == CCB_PHYS (cp, phys)) {
+				kprintf ("%s: timeout on connected nccb=%p,"
+					" resetting bus\n", ncr_name (np), cp);
+				ncr_init (np, "connected ccb timeout", HS_FAIL);
+				break;
+			}
 
 			switch (cp->host_status) {
 
 			case HS_BUSY:
@@ -5701,6 +5713,16 @@ static void ncr_int_ma (ncb_p np, u_char dstat)
 	if (cp != np->header.cp) {
 	    kprintf ("%s: SCSI phase error fixup: CCB address mismatch "
 		    "(%p != %p) np->nccb = %p\n",
 		    ncr_name (np), (void *)cp, (void *)np->header.cp,
 		    (void *)np->link_nccb);
 /*	    return;*/
 	}
+
+	/*
+	**	DF-3607: never restart the script processor from an nccb that
+	**	has already been completed (e.g. by the timeout daemon) and
+	**	possibly re-used for another command.  Resuming would DMA from
+	**	stale scatter descriptors into released buffers.
+	*/
+	if (cp->magic != CCB_MAGIC || cp->ccb == NULL) {
+		kprintf ("%s: SCSI phase error fixup on dead nccb (%p),"
+			" resetting bus\n", ncr_name (np), cp);
+		ncr_init (np, "phase fixup on dead ccb", HS_FAIL);
+		return;
+	}
 
 	/*
 	**	find the interrupted script command,
 	**	and the address at which to continue.