⬢ DragonFlyBSD Kernel Audit
DF-3608 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/ncr/ncr.c
+++ b/sys/dev/disk/ncr/ncr.c
@@ -4090,6 +4090,20 @@ ncr_action (struct cam_sim *sim, union ccb *ccb)
 		/*
 		**	command
 		*/
 		/* XXX JGibbs - Support other command types */
+		/*
+		**	DF-3608: the 53C8xx DMA engine and every descriptor we
+		**	build for it (struct link, struct scr_tblmove, squeue[])
+		**	are 32-bit.  On amd64 with memory above 4GB, silently
+		**	truncating vtophys() results makes the chip transfer to
+		**	the wrong physical memory.  Refuse anything that does
+		**	not fit, instead of corrupting memory.
+		*/
+		if (((u_int64_t)vtophys (csio->cdb_io.cdb_bytes) >> 32) != 0
+		 || ((u_int64_t)vtophys (&csio->sense_data) >> 32) != 0) {
+			ccb->ccb_h.status = CAM_REQ_TOO_BIG;
+			ncr_free_nccb (np, cp);
+			crit_exit ();
+			xpt_done (ccb);
+			return;
+		}
 		cp->phys.cmd.addr	= vtophys (csio->cdb_io.cdb_bytes);
 		cp->phys.cmd.size	= csio->cdb_len;
@@ -6564,7 +6578,17 @@ ncr_alloc_nccb (ncb_p np, u_long target, u_long lun)
 	/*
 	**	Allocate a nccb
 	*/
-	cp = kmalloc (sizeof (struct nccb), M_DEVBUF, M_WAITOK | M_ZERO);
+	/*
+	**	DF-3608: keep the nccb below 4GB: cp->p_nccb / CCB_PHYS()
+	**	feed 32-bit script descriptors (jump_nccb.l_paddr, squeue[]).
+	*/
+	cp = kmem_alloc_contig (round_page(sizeof (struct nccb)), 0,
+				0ul, 0xfffffffful, PAGE_SIZE);
+	if (cp != NULL)
+		bzero (cp, sizeof (struct nccb));
 
 	if (DEBUG_FLAGS & DEBUG_ALLOC) { 
 		kprintf ("new nccb @%p.\n", cp);
--- a/sys/dev/disk/ncr/ncr.c
+++ b/sys/dev/disk/ncr/ncr.c
@@ -6700,6 +6700,13 @@ ncr_scatter
 		phys->data[segment].addr = segaddr;
 		phys->data[segment].size = segsize;
 		segment++;
+		/*
+		**	DF-3608: reject segments whose physical address cannot
+		**	be represented in the 32-bit scr_tblmove.addr field.
+		*/
+		if (((u_int64_t)segaddr >> 32) != 0)
+			return (-1);
 	}
 
 	if (datalen) {