⬢ DragonFlyBSD Kernel Audit
DF-5370 / fix.diff
← back to finding ↓ download raw
--- a/sys/platform/vkernel64/platform/busdma_machdep.c
+++ b/sys/platform/vkernel64/platform/busdma_machdep.c
@@ -385,6 +385,15 @@
 	if (map != NULL) {
 		if (STAILQ_FIRST(&map->bpages) != NULL)
 			return (EBUSY);
+		/*
+		 * A map queued for a deferred load (EINPROGRESS) always
+		 * has pagesneeded != 0 and sits on the bounce zone's
+		 * waiting list.  Freeing it here leaves a dangling
+		 * reference that get_map_waiting()/reserve_bounce_pages()
+		 * later dereference (use-after-free).
+		 */
+		if (map->pagesneeded != 0 || map->pagesreserved != 0)
+			return (EBUSY);
 		kfree(map, M_DEVBUF);
 	}
 	dmat->map_count--;
@@ -892,11 +901,29 @@
 _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
 {
 	struct bounce_page *bpage;
+	struct bounce_zone *bz;
 
 	while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
 		STAILQ_REMOVE_HEAD(&map->bpages, links);
 		free_bounce_page(dmat, bpage);
 	}
+
+	/*
+	 * If the map is queued for a deferred load, dequeue it and drop
+	 * its bounce page reservation, otherwise the deferred machinery
+	 * later wakes a map the driver considers dead (and the ghost
+	 * reservation permanently wedges the zone).
+	 */
+	if (map->pagesneeded != 0 || map->pagesreserved != 0) {
+		bz = dmat->bounce_zone;
+		if (bz != NULL) {
+			BZ_LOCK(bz);
+			STAILQ_REMOVE(&bz->bounce_map_waitinglist, map,
+				      bus_dmamap, links);
+			BZ_UNLOCK(bz);
+		}
+		return_bounce_pages(dmat, map);
+	}
 }
 
 void