DF-5370 / fix.diff
--- 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 |