# DF-2204 — amdgpu_xgmi: no device-removal path, stale/UAF; non-idempotent add (Low)

## Claim
Two related defects in `sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c`:

1. **No removal path.**  There is no `amdgpu_xgmi_remove_device()` /
   `amdgpu_xgmi_fini()` anywhere in the tree (zero `list_del` of
   `xgmi.head` — confirmed by grep).  Once a device is
   `list_add_tail()`-ed into `hive->device_list` (line 85) it stays linked
   forever, even after `amdgpu_device` is torn down and freed.  Any later
   `amdgpu_xgmi_add_device()` for any device in the same hive then
   iterates the freed node (UAF) via `list_for_each_entry` at lines 86
   and 98.

2. **Non-idempotent add.**  `amdgpu_xgmi_add_device()` never checks
   whether `adev` is already linked.  A second call (e.g. driver reset /
   rebind re-entering `amdgpu_device_init` without first removing the
   node) `list_add_tail()`s an already-linked node, corrupting the
   doubly-linked list (infinite loops / visits to arbitrary addresses).

## Verification approach
**HW-gated / source-only.**  Reachable only on Vega20 hardware with a
driver reset / unbind-rebind cycle.  No GPU on this audit guest.  Per the
task brief, source-only confirmation is acceptable.

1. `grep` the entire `sys/dev/drm` tree for `xgmi.head` /
   `amdgpu_xgmi_remove` / `amdgpu_xgmi_fini` — confirm the only
   `list_add_tail`/iterators are in `amdgpu_xgmi.c` and there is no
   `list_del`.
2. Confirm `list_add_tail` is unconditional (no `list_empty` check).
3. Confirm `amdgpu_device_fini()` (the natural teardown counterpart to
   `amdgpu_device_init`'s `amdgpu_xgmi_add_device()` call) does *not*
   call any xgmi cleanup.
4. Author `fix.diff` (idempotency guard + new `amdgpu_xgmi_remove_device`
   + hook into `amdgpu_device_fini`).
5. **Phase 8** — apply all 5 batched fixes and rebuild `amdgpu.ko` with
   `-Werror`.

## Source trace (confirmed)
* `sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c:85` — only `list_add_tail(&adev->gmc.xgmi.head, ...)` in the tree.
* `sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c:86,98` — only iterators over `hive->device_list`.
* `grep -rn 'list_del.*xgmi.head\|amdgpu_xgmi_remove\|amdgpu_xgmi_fini' sys/dev/drm` → **0 matches** (confirmed).
* `sys/dev/drm/amd/amdgpu/amdgpu_device.c:1691` — call to `amdgpu_xgmi_add_device(adev)` (init).
* `sys/dev/drm/amd/amdgpu/amdgpu_device.c:2727-2779` — `amdgpu_device_fini()` does **not** call any xgmi teardown before freeing `adev`.
* `sys/dev/drm/amd/amdgpu/amdgpu.h:1252` — only `amdgpu_xgmi_add_device` is declared; no remove counterpart.

## Files
* `VERDICT.md` — full narrative.
* `fix.diff` — three-part fix in three files:
  * `amdgpu_xgmi.c`: add idempotency guard (`if (!list_empty(...)) goto exit`) before the existing `list_add_tail`; append `amdgpu_xgmi_remove_device()` that does `list_del` + `INIT_LIST_HEAD` under the `xgmi_mutex`.
  * `amdgpu.h`: declare `void amdgpu_xgmi_remove_device(struct amdgpu_device *adev);`.
  * `amdgpu_device.c`: call `amdgpu_xgmi_remove_device(adev)` near the top of `amdgpu_device_fini()`, before any of the device's memory is freed.
* `build_baseline.log` — unpatched `amdgpu.ko` build, rc=0, `-Werror`.
* `build_patched.log` — patched `amdgpu.ko` rebuild (`amdgpu_xgmi.o` + `amdgpu_device.o` recompiled), rc=0, `-Werror`.
* `env.txt` — guest environment.

## Reproduce
```
./build.sh    # applies fix.diff + rebuilds amdgpu.ko
./run.sh      # HW-gated no-op (see VERDICT.md)
```
