DF-2206 / fix.diff
diff --git a/sys/dev/drm/linux_iomapping.c b/sys/dev/drm/linux_iomapping.c index 0000000..1111111 100644 --- a/sys/dev/drm/linux_iomapping.c +++ b/sys/dev/drm/linux_iomapping.c @@ -65,6 +65,21 @@ int indx; vm_paddr_t paddr_end; + /* + * DF-2206: The whole lookup + teardown + removal + free must run + * under iomap_lock. The list is process-global (shared across every + * DRM device), so two concurrent iounmap() callers can otherwise + * race: one frees an entry the other is still walking (UAF read / + * wild pointer), or both pass the unlocked found-check and double-call + * pmap_unmapdev()/SLIST_REMOVE on the same VA (double free / NULL + * deref when SLIST_REMOVE walks past end of list). + * + * iomap_lock is initialised LK_CANRECURSE, and none of the called + * helpers (pmap_unmapdev, pmap_change_attr, kfree) acquire it, so + * holding it across the teardown is safe. + */ + lockmgr(&iomap_lock, LK_EXCLUSIVE); + SLIST_FOREACH_MUTABLE(imp, &iomap_list, im_iomaps, tmp_imp) { if (imp->pmap_addr == ptr) { found = 1; @@ -73,6 +88,7 @@ } if (!found) { + lockmgr(&iomap_lock, LK_RELEASE); kprintf("iounmap: invalid address %p\n", ptr); return; } @@ -95,7 +111,6 @@ pmap_unmapdev((vm_offset_t)imp->pmap_addr, imp->npages * PAGE_SIZE); - lockmgr(&iomap_lock, LK_EXCLUSIVE); SLIST_REMOVE(&iomap_list, imp, iomap, im_iomaps); lockmgr(&iomap_lock, LK_RELEASE); |