DragonFlyBSD Kernel Audit
DF-1035 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/platform/pc64/x86_64/busdma_machdep.c b/sys/platform/pc64/x86_64/busdma_machdep.c
--- a/sys/platform/pc64/x86_64/busdma_machdep.c
+++ b/sys/platform/pc64/x86_64/busdma_machdep.c
@@ -938,8 +938,6 @@
 	pmap_t pmap;
 	bus_dma_segment_t cache_segments[BUS_DMA_CACHE_SEGMENTS];
 	bus_dma_segment_t *segments;
-	bus_dma_segment_t *segs;
-	int nsegs_left;
 
 	if (dmat->nsegments <= BUS_DMA_CACHE_SEGMENTS)
 		segments = cache_segments;
@@ -958,8 +956,6 @@
 	resid = (bus_size_t)uio->uio_resid;
 	iov = uio->uio_iov;
 
-	segs = segments;
-	nsegs_left = dmat->nsegments;
 
 	if (uio->uio_segflg == UIO_USERSPACE) {
 		struct thread *td;
@@ -986,29 +982,39 @@
 		caddr_t addr = (caddr_t) iov[i].iov_base;
 
 		error = _bus_dmamap_load_buffer(dmat, map, addr, minlen,
-				segs, nsegs_left,
+				segments, dmat->nsegments,
 				pmap, flags, &lastaddr, &nsegs, first);
 		first = 0;
 
 		resid -= minlen;
-		if (error == 0) {
-			nsegs_left -= nsegs;
-			segs += nsegs;
-		}
+		/*
+		 * _bus_dmamap_load_buffer treats *segp (= &nsegs) as a
+		 * 1-based ABSOLUTE index into the array passed via the
+		 * 'segments' parameter (line 664: sg = &segments[seg-1]).
+		 * We must therefore pass the CONSTANT segments pointer and
+		 * dmat->nsegments on every call, exactly like
+		 * bus_dmamap_load_mbuf_segment() does.  The previous code
+		 * advanced segs and shrank nsegs_left between iovecs but
+		 * kept *segp absolute, which tripped the KKASSERT at line
+		 * 662 (INVARIANTS) or wrote past the segments array (noinv).
+		 * _bus_dmamap_load_buffer returns EFBIG when it runs out of
+		 * segments, so we still terminate cleanly.
+		 */
 	}
 
+
 	/*
-	 * Minimum one DMA segment, even if 0-length buffer.
+	 * nsegs is the absolute 1-based end-index of the last segment
+	 * written (set by _bus_dmamap_load_buffer via *segp), so it is
+	 * always >= 1, satisfying the historical "minimum one DMA segment
+	 * even if 0-length buffer" requirement without further arithmetic.
 	 */
-	if (nsegs_left == dmat->nsegments)
-		--nsegs_left;
-
 	if (error) {
 		/* force "no valid mappings" in callback */
 		callback(callback_arg, segments, 0,
 			 0, error);
 	} else {
-		callback(callback_arg, segments, dmat->nsegments - nsegs_left,
+		callback(callback_arg, segments, nsegs,
 			 (bus_size_t)uio->uio_resid, error);
 	}
 	if (dmat->nsegments > BUS_DMA_CACHE_SEGMENTS)