No device-removal path and non-idempotent add: stale/UAF list nodes and list corruption on re-add
Summary
No amdgpu_xgmi_remove_device/amdgpu_xgmi_fini anywhere in tree (zero list_del of xgmi.head). Once device list_add_tail-d into hive (line 85) stays forever even after amdgpu_device torn down and freed. Later amdgpu_xgmi_add_device for any device in same hive iterates freed node (UAF) via list_for_each_entry (lines 86 and 98). Separately add is not idempotent: never checks whether adev already linked so second call list_add_tail on already-linked node corrupts doubly-linked list can turn loops into infinite loops or visits to arbitrary addresses. Trigger: root unbind/rebind GPU or driver reset path re-enters amdgpu_device_init without first removing node. Impact: kernel memory corruption UAF read/write of freed amdgpu_device or list corruption infinite loop panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2204 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | claim, exhaustive-grep evidence, fix summary | 3.4 KB | β raw |
| VERDICT.md | verdict | no-remove-path + non-idempotent-add trace + 3-file fix | 3.7 KB | β raw |
| fix.diff | suggested-fix | idempotency guard + new amdgpu_xgmi_remove_device + fini hook (xgmi.c, amdgpu.h, amdgpu_device.c) | 2.7 KB | view raw |
| build.sh | build-script | applies fix.diff + rebuilds amdgpu.ko in guest | 564 B | view raw |
| run.sh | run-script | HW-gated no-op runner | 414 B | view raw |
| build_baseline.log | build-log | unpatched amdgpu.ko build, rc=0, -Werror | 1.0 MB | β download |
| build_patched.log | build-log | patched amdgpu.ko rebuild, amdgpu_xgmi.o + amdgpu_device.o recompiled, rc=0, -Werror | 12.6 KB | view raw |
| env.txt | environment | uname, cc, kern.version | 376 B | view raw |
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:
-
No removal path. There is no
amdgpu_xgmi_remove_device()/amdgpu_xgmi_fini()anywhere in the tree (zerolist_delofxgmi.headβ confirmed by grep). Once a device islist_add_tail()-ed intohive->device_list(line 85) it stays linked forever, even afteramdgpu_deviceis torn down and freed. Any lateramdgpu_xgmi_add_device()for any device in the same hive then iterates the freed node (UAF) vialist_for_each_entryat lines 86 and 98. -
Non-idempotent add.
amdgpu_xgmi_add_device()never checks whetheradevis already linked. A second call (e.g. driver reset / rebind re-enteringamdgpu_device_initwithout 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.
grepthe entiresys/dev/drmtree forxgmi.head/amdgpu_xgmi_remove/amdgpu_xgmi_finiβ confirm the onlylist_add_tail/iterators are inamdgpu_xgmi.cand there is nolist_del.- Confirm
list_add_tailis unconditional (nolist_emptycheck). - Confirm
amdgpu_device_fini()(the natural teardown counterpart toamdgpu_device_init'samdgpu_xgmi_add_device()call) does not call any xgmi cleanup. - Author
fix.diff(idempotency guard + newamdgpu_xgmi_remove_device+ hook intoamdgpu_device_fini). - Phase 8 β apply all 5 batched fixes and rebuild
amdgpu.kowith-Werror.
Source trace (confirmed)
sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c:85β onlylist_add_tail(&adev->gmc.xgmi.head, ...)in the tree.sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c:86,98β only iterators overhive->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 toamdgpu_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 freeingadev.sys/dev/drm/amd/amdgpu/amdgpu.h:1252β onlyamdgpu_xgmi_add_deviceis 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 existinglist_add_tail; appendamdgpu_xgmi_remove_device()that doeslist_del+INIT_LIST_HEADunder thexgmi_mutex.amdgpu.h: declarevoid amdgpu_xgmi_remove_device(struct amdgpu_device *adev);.amdgpu_device.c: callamdgpu_xgmi_remove_device(adev)near the top ofamdgpu_device_fini(), before any of the device's memory is freed.build_baseline.logβ unpatchedamdgpu.kobuild, rc=0,-Werror.build_patched.logβ patchedamdgpu.korebuild (amdgpu_xgmi.o+amdgpu_device.orecompiled), 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)
DF-2204 β VERDICT
Verdict: REPRODUCED (source-only, HW-gated).
Class: UAF + list corruption (memory corruption).
Mechanism
Two related lifetime defects in the amdgpu XGMI bookkeeping.
(1) No removal path β stale node β UAF
amdgpu_xgmi_add_device() (sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c) links
a device into a hive:
/* amdgpu_xgmi.c:85 */
list_add_tail(&adev->gmc.xgmi.head, &hive->device_list);
There is no counterpart anywhere in the tree. Verified by exhaustive grep
of sys/dev/drm:
$ grep -rn 'list_del.*xgmi\.head\|amdgpu_xgmi_remove\|amdgpu_xgmi_fini' sys/dev/drm (0 matches)
The only operations on xgmi.head in the entire tree are the
list_add_tail at line 85 and the two iterators at lines 86 and 98.
The natural teardown counterpart of the init call
(amdgpu_device_init β amdgpu_xgmi_add_device(adev) at
amdgpu_device.c:1691) is amdgpu_device_fini()
(amdgpu_device.c:2727). Reading the whole function (lines 2727-2779)
confirms it never calls any xgmi cleanup. So once a Vega20 GPU is added
to a hive, its gmc.xgmi.head node stays linked into
hive->device_list forever β across kfree(adev), across module unload,
across driver rebind.
The next amdgpu_xgmi_add_device() for any other device in the same hive
then list_for_each_entry-walks hive->device_list (lines 86 and 98)
and dereferences the freed amdgpu_device β classic UAF read.
(2) Non-idempotent add β list corruption
list_add_tail() at line 85 is unconditional. There is no
list_empty(&adev->gmc.xgmi.head) guard. A second
amdgpu_xgmi_add_device() for the same adev (which a driver reset /
unbind-rebind path will trigger, because amdgpu_device_init re-runs
without first removing the node) re-links an already-linked node. In a
doubly-linked list, that produces a corrupted structure: the same node
appears twice, loops can become infinite, or iterators visit arbitrary
addresses.
Trigger surface / reachability
amdgpu_xgmi_add_device() early-returns for non-Vega20 ASICs and APUs,
so this is Vega20-only. The realistic trigger is a driver reset / rebind
cycle on a Vega20 host (root or root-equivalent; unbind/rebind of a PCI
device). On this audit guest there is no GPU, so the UAF / list
corruption cannot be exercised live; the bug is confirmed at the source
level and the fix compiles cleanly (Phase 8).
Fix
fix.diff is a three-part patch:
amdgpu_xgmi.c(part 1): add an idempotency guard before the existinglist_add_tailβ ifadev->gmc.xgmi.headis not empty the device is already linked, log a warning andgoto exitwithret = 0.amdgpu_xgmi.c(part 2): append a newamdgpu_xgmi_remove_device(adev)that, under the existingxgmi_mutex, doeslist_del+INIT_LIST_HEADif the node is linked.amdgpu.h: declare the new function next toamdgpu_xgmi_add_device.amdgpu_device.c: callamdgpu_xgmi_remove_device(adev)near the top ofamdgpu_device_fini()(right afteradev->shutdown = true), before any of the device's memory or MMIO is freed.
Phase 8 build validation
Applied fix.diff (plus the four other batched DRM fixes) to the in-guest
/usr/src, rebuilt amdgpu.ko with -Werror:
* baseline (unpatched) amdgpu.ko: rc=0 (build_baseline.log).
* patched amdgpu.ko: rc=0, amdgpu_xgmi.o and amdgpu_device.o
recompiled (build_patched.log); no warnings, no errors.
Verdict
REPRODUCED at source level (HW-gated; no live repro possible on guest).
Both halves of the claim β no removal path and non-idempotent add β are
confirmed by exhaustive grep and by reading amdgpu_device_fini
end-to-end. The fix compiles cleanly under -Werror.
Fix verification
fixedamdgpu.ko baseline+patched rc=0 -Werror (3-file fix)
amdgpu.ko baseline+patched rc=0 -Werror (3-file fix)
Confirmed kernel references
β
Detail
Exploit chain
none (HW-gated)
Evidence (decisive lines)
HW-gated. Source-confirmed: no list_del for xgmi.head anywhere -> stale UAF + non-idempotent add.
Verified recommended fix
HW-gated. Source-confirmed: no list_del for xgmi.head anywhere -> stale UAF + non-idempotent add.
Verdict
HW-gated. Source-confirmed: no list_del for xgmi.head anywhere -> stale UAF + non-idempotent add.
No comments yet.