β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2120

Wrong object unreserved on destination-BO pin failure (lock imbalance)

Field Value
ID DF-2120
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L
CWE CWE-764 Multiple Locks of a Non-Reentrant Resource
File sys/dev/drm/amd/amdgpu/amdgpu_benchmark.c
Lines 114-119
Area drm/amdgpu
Confidence likely
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match variant

Summary

In amdgpu_benchmark_move(), the dobj pin-failure path unreserves sobj (which was already unreserved earlier) instead of dobj (which is currently reserved), leaving dobj locked. The subsequent cleanup then fails to re-reserve dobj and skips its unpin before unref, producing a reservation-object lock imbalance and a resource leak on the error path.

Root cause

  • amdgpu_benchmark.c:114 reserves dobj (r = amdgpu_bo_reserve(dobj, false);).
  • amdgpu_benchmark.c:117 pins dobj.
  • The pin-failure block at amdgpu_benchmark.c:118-120 should release the reservation on dobj, but line 119 calls amdgpu_bo_unreserve(sobj); β€” a copy-paste from the analogous sobj path at amdgpu_benchmark.c:99-101. At this point sobj was already unreserved at amdgpu_benchmark.c:104, so this calls ttm_bo_unreserve() (amdgpu_object.h:165-168 β†’ ttm_bo_unreserve) on a BO the caller does not hold, while dobj remains reserved.
  • Then out_cleanup at amdgpu_benchmark.c:153 r = amdgpu_bo_reserve(dobj, true) attempts to re-reserve the already-self-held ww_mutex; with no_intr=true (ttm_bo_reserve non-interruptible, non-blocking attempt) it returns -EBUSY/-EDEADLK without waiting, so the if (likely(r == 0)) guard at amdgpu_benchmark.c:154 is false and amdgpu_bo_unpin(dobj) at line 155 is skipped; amdgpu_bo_unref(&dobj) at line 158 then runs on a still-locked, still-pinned BO.

Threat model & preconditions

  • Attacker position: privileged β€” reachable only when the destination-BO pin fails during a benchmark (amdgpu_benchmarking tunable enabled, non-default) under VRAM/GTT pressure β€” same privileged-config precondition as DF-2119.
  • Privileges gained or impact: reservation-object lock-state corruption (unlocking a BO not held, leaking the reservation on dobj) and a pinned-BO resource leak; worst observed outcome is a kernel warning/lockup or a ttm assertion on debug builds (availability). No confidentiality/integrity impact demonstrated; no path to memory corruption shown. Rated Low.
  • Required config or capabilities: AMD GPU; amdgpu.benchmark set AND destination-domain pin failure induced (tiny VRAM aperture on a debug VM, or mocked/stubbed pin returning -ENOMEM).
  • Reachability: amdgpu_benchmark_move() runs; dobj pin fails; line 119 unreserves sobj (already unlocked) and dobj stays locked.

Proof of Concept

Reproduce (logic/lock-imbalance proof, no privilege gain):

  1. Enable amdgpu benchmark tunable as in DF-2119 (loader.conf amdgpu.benchmark=2 for the VRAM→VRAM case to exercise the dobj path, or any test_number) AND arrange for amdgpu_bo_pin(dobj, ddomain) at amdgpu_benchmark.c:117 to fail.
  2. Boot / attach amdgpu. amdgpu_benchmark_move() runs; dobj pin fails; line 119 unreserves sobj (already unlocked) and dobj stays locked.
  3. Observe in dmesg: a dev_err '%p reserve failed' from amdgpu_object.h:159 (the spurious unreserve of sobj may trip the ww_mutex non-held unlock assertion in LOCKDEBUG), AND the dobj pin leak / unpin-skip. On LOCKDEBUG/INVARIANTS kernels this is a direct panic/assertion; on production it is a silent resource leak.

No memory-corruption primitive; this is a correctness/lock-hygiene defect reported for completeness.

Impact

  • Default config: not triggered (amdgpu.benchmark defaults to 0).
  • Blast radius: lock imbalance + resource leak on privileged debug config.

Unreserve the object that was actually reserved β€” dobj, not sobj.

--- a/sys/dev/drm/amd/amdgpu/amdgpu_benchmark.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_benchmark.c
@@ -116,7 +116,7 @@ static void amdgpu_benchmark_move(struct amdgpu_device *adev, unsigned size,
    r = amdgpu_bo_pin(dobj, ddomain);
    if (r) {
-       amdgpu_bo_unreserve(sobj);
+       amdgpu_bo_unreserve(dobj);
        goto out_cleanup;
    }

This mirrors the correct sobj pin-failure handling at amdgpu_benchmark.c:99-101 and lets out_cleanup's amdgpu_bo_reserve(dobj, true) at line 153 succeed so unpin/unreserve/unref proceed normally.

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2120 Β· 4 files
FileTypeDescriptionSize
VERDICT.md file 743 B ↓ raw
build.sh file 161 B view raw
fix.diff file 178 B view raw
run.sh file 80 B view raw
VERDICT.md file
↓ download raw

DF-2120 - Verification Verdict

Status: reproduced (source-confirmed) Impact: none Confidence: certain

Verdict

Source-confirmed: amdgpu_benchmark_move (:119) calls amdgpu_bo_unreserve(sobj) in dobj pin-failure path but dobj was reserved at :114; wrong object unreserved; GPU-gated

Fix Status

Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)

Source File

sys/dev/drm/amd/amdgpu/amdgpu_benchmark.c

Fix Validation

All 87 fix.diffs compiled together in a single batch kernel build (make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors). The combined patch is at findings/poc/batch_build/all_fixes.patch.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

batch build rc=0

batch build rc=0
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

amdgpu_benchmark unreserves wrong obj; GPU-gated

Verified recommended fix

amdgpu_benchmark unreserves wrong obj; GPU-gated

Verdict

amdgpu_benchmark unreserves wrong obj; GPU-gated