# DF-4164 — `radeon_unmap_vram_bos()` walks `rdev->gem.objects` without `gem.mutex` while unprivileged GEM create/close mutates and frees it

File: `sys/dev/drm/radeon/radeon_pm.c` (pass 2)

## What
`radeon_unmap_vram_bos()` (radeon_pm.c:146-157), called from
`radeon_pm_set_clocks()` (:279) on every legacy PM clock change, walks the
global GEM object list:

```c
list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
        if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
                ttm_bo_unmap_virtual(&bo->tbo);
}
```

with **no lock and no per-object reference**. Every mutation of that list is
done under `rdev->gem.mutex` by its writers:

* GEM create: `radeon_gem_object_create` → `list_add_tail(&robj->list, ...)`
  (radeon_gem.c:89-91),
* GEM free: `radeon_ttm_bo_destroy` → `list_del_init(&bo->list)` then
  `kfree(bo)` (radeon_object.c:68-85),
* force-delete (radeon_object.c:441-443).

Both sides are reachable from **unprivileged** contexts: any local user with
access to the radeon drm node allocates GEM VRAM objects (ioctls) and frees
them by closing/unmapping handles — `radeon_ttm_bo_destroy` runs from TTM when
the last reference drops, concurrent with the PM path.

The `_safe` iterator is not sufficient: it only guards removal of the element
currently pointed at (and even that assumes serialized writes); racing
insertions and the `kfree` of `n`'s target turn the walk into a UAF read of
`bo->tbo.mem.mem_type` / `bo->tbo` and potential list corruption.

## Reachability of the PM side on DFly
`radeon_pm_set_clocks` is called from `radeon_pm_compute_clocks_old`
(:1755/:1767/:1778/:1801 — pm_method PROFILE/DYNPM), the ACPI event handler
(:88 — AC plug/unplug under `PM_PROFILE_AUTO`), `radeon_pm_fini_old` (:1660),
and `radeon_pm_compute_clocks` from radeon_device.c:1816/:1929 (fbcon init /
resume). DPM-method machines do **not** reach it
(`radeon_dpm_change_power_state_locked` never calls the unmap helper), so the
exposed population is: pre-DPM asics (r100–rv570/rs4xx) and any card that fell
back to `PM_METHOD_PROFILE` because rlc/smc firmware is missing
(radeon_pm.c:1533-1542/1571-1582). PM_METHOD_DYNPM is unreachable on DFly
(only settable via the `#ifdef DUMBBELL_WIP` sysfs attrs, :354-786).

Concrete unprivileged scenario: console user runs an X session (GEM churn:
create/close loops) on a profile-mode radeon while AC attach/detach events
(or a resume) drive clock switches → race.

## Fix
Hold `rdev->gem.mutex` across the walk (fix.diff). All existing `gem.mutex`
holders only guard pure list operations (never nested with ttm/reservation
locks), so wrapping `ttm_bo_unmap_virtual()` introduces no lock-order
inversion.

## Status on this audit guest
Hardware-gated (no radeon GPU — env.txt). Source-structure certain, timing
race → inconclusive per family convention.
