amdgpu_gfx_kiq_init calls amdgpu_bo_unreserve on unreserved BO (ttm reservation imbalance)
- File:
sys/dev/drm/amd/amdgpu/amdgpu_gfx.c - Lines: 299β303
- Severity: Info
- CVSS 3.1:
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U:C:N/I:N/A:L - CWE: CWE-664 Improper control of a resource through its lifetime, CWE-820 missing sync of paired lock/unlock
- Confidence: likely
- Status: new
Summary
In amdgpu_gfx_kiq_init, if amdgpu_bo_reserve(kiq->eop_obj, true) returns
non-zero, the code only dev_warn's and then unconditionally falls through to
amdgpu_bo_kunmap + amdgpu_bo_unreserve.
Calling amdgpu_bo_unreserve (β ttm_bo_unreserve) on a BO that was never
reserved is an unbalanced reservation that TTM treats as a misuse.
Root cause
amdgpu_gfx.c:299-303:
r = amdgpu_bo_reserve(kiq->eop_obj, true);
if (unlikely(r != 0))
dev_warn(...); // no return / no goto
amdgpu_bo_kunmap(kiq->eop_obj);
amdgpu_bo_unreserve(kiq->eop_obj);
The error branch lacks return r; (or a goto skipping unreserve).
amdgpu_bo_unreserve (amdgpu_object.h:165-168) unconditionally calls
ttm_bo_unreserve(&bo->tbo) with no held-reservation check, so on reserve
failure this releases a reservation that is not held.
Threat model
Effectively unreachable. The BO was just created and pinned by
amdgpu_bo_create_kernel at lines 289-291; nothing else can hold its
reservation, so amdgpu_bo_reserve(..., true) succeeds in practice.
If it ever did fail (e.g. -ENOMEM deep in ttm_bo_reserve under extreme
memory pressure), the result is a TTM reservation imbalance / kernel warning,
i.e. local DoS at worst, during driver load β not controllable by an
unprivileged user.
Recommended fix
Return on reserve failure so kunmap/unreserve are only called when the
reservation is actually held:
--- a/sys/dev/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_gfx.c
@@ -299,7 +299,7 @@ int amdgpu_gfx_kiq_init(struct amdgpu_device *adev,
r = amdgpu_bo_reserve(kiq->eop_obj, true);
if (unlikely(r != 0)) {
dev_warn(adev->dev, "(%d) reserve kiq eop bo failed\n", r);
- }
+ return r;
+ }
amdgpu_bo_kunmap(kiq->eop_obj);
amdgpu_bo_unreserve(kiq->eop_obj);
(Note: the upstream Linux version of this routine does not reserve at all here; matching that is also acceptable.)
References
sys/dev/drm/amd/amdgpu/amdgpu_gfx.c:299-303β missing early return on reserve failuresys/dev/drm/amd/amdgpu/amdgpu_object.h:165-168βamdgpu_bo_unreservehas no held-check
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1998 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-only confirmation + mechanism + fix | 1.6 KB | β raw |
| fix.diff | suggested-fix | Move amdgpu_bo_kunmap/unreserve inside if (r == 0) branch. | 667 B | view raw |
| ../fix_build_new.log | build-log | Batch kernel build with new fixes (rc=0, -Werror) | 5.6 MB | β download |
DF-1998 β PoC Verification Verdict
Category: drm (module / HW-gated)
Source: sys/dev/drm/amd/amdgpu/amdgpu_gfx.c:299-303
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR)
Date verified: 2026-07-25
Verdict: REPRODUCED (source-only confirmation; HW/module-gated)
Mechanism
amdgpu_gfx_kiq_init: r = amdgpu_bo_reserve(kiq->eop_obj, true); if (r != 0) dev_warn(...); amdgpu_bo_kunmap(kiq->eop_obj); amdgpu_bo_unreserve(kiq->eop_obj); The unreserve runs unconditionally even when reserve failed β releasing a reservation the caller doesn't hold.
In GENERIC kernel build: NO (module / not compiled into X86_64_GENERIC on audit QEMU guest)
Reproduction status
This finding is hardware/module gated: the vulnerable code path requires specific hardware (AMD GPU / radeon / Atheros NIC / RAID controller) or a loadable module not present on the audit QEMU guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not exercised. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.
Fix
Move amdgpu_bo_kunmap/unreserve inside if (r == 0) branch.
See fix.diff for the standalone git-apply-able unified diff. Validated by applying the 38 new-finding batch diffs (including this one) and building a single X86_64_GENERIC kernel (rc=0, -Werror clean).
Fix verification
fixedVALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
REPRODUCED (source-only): amdgpu_gfx_kiq_init: r=amdgpu_bo_reserve(kiq->eop_obj,true); if (r!=0) dev_warn(...); amdgpu_bo_kunmap(kiq->eop_obj); amdgpu_bo_unreserve(kiq->eop_obj); unreserve runs uncond
Verified recommended fix
REPRODUCED (source-only): amdgpu_gfx_kiq_init: r=amdgpu_bo_reserve(kiq->eop_obj,true); if (r!=0) dev_warn(...); amdgpu_bo_kunmap(kiq->eop_obj); amdgpu_bo_unreserve(kiq->eop_obj); unreserve runs unconditionally even when reserve failed.
Verdict
REPRODUCED (source-only): amdgpu_gfx_kiq_init: r=amdgpu_bo_reserve(kiq->eop_obj,true); if (r!=0) dev_warn(...); amdgpu_bo_kunmap(kiq->eop_obj); amdgpu_bo_unreserve(kiq->eop_obj); unreserve runs unconditionally even when reserve failed.
No comments yet.