{
  "finding_id": "DF-5138",
  "title": "ttm_eu_reserve_buffers() duplicate-BO (-EALREADY/dups) handling is dead code on DragonFly: linux_wwmutex.c never returns -EALREADY (LK_CANRECURSE silent recursion), so duplicate BOs from an unprivileged CS list are recursively double-locked and ww ticket accounting desyncs",
  "status": "reproduced",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Mechanism runtime-reproduced on the audit guest with a KLD that performs ttm_eu_reserve_buffers exact duplicate-BO sequence: ww_mutex_lock twice with the same ticket returned 0 the second time (Linux contract: -EALREADY=-37; no -EALREADY producer exists in sys/dev/drm/linux_wwmutex.c because ww_mutex_init sets LK_CANRECURSE at linux_wwmutex.c:62 and kern_lock.c:125-143 silently recurses same-thread exclusive relocks), making the dups branch at ttm_execbuf_util.c:119-124 dead code; after the two matching unlocks ticket->acquired stayed 1 instead of 0 (ww_mutex_unlock at linux_wwmutex.c:188-202 decrements only the first unlock of a recursive hold). Success path is lock-count balanced (ww_mutex_is_locked==0 after) \u2014 NOT a direct corruption primitive. Traced ceilings: (a) duplicate BO + reservation_object_reserve_shared failure (krealloc -ENOMEM, linux_reservation.c:93-94) leaves the second recursive episode permanently held (DF-2136-class wedge reachable through a user-controlled duplicate CS entry; later CS users of the BO block forever, unkillable for intr=false callers like amdgpu_gem.c:177); (b) acquired over-count forces the NOWAIT+tsleep route on later same-ticket locks. Duplicate handles are deliverable by unprivileged userspace on both consumers (radeon_cs.c:103-104 per-entry lookup; amdgpu_bo_list.c no dedupe). Full chain needs GPU hardware the audit guest lacks (0 AMD/ATI PCI devices), hence impact=none here and severity Low. fix.diff (restore -EALREADY in __wwlock) applied in guest /usr/src, drm.ko rebuilt clean (-Werror) and reloaded: proof printed second_lock=-37, acquired=0, RESULT=FIXED \u2014 bad behavior gone.",
  "exploit_chain": "conditional local DoS on GPU-equipped systems: CS ioctl listing the same GEM handle twice -> ttm_eu_reserve_buffers recursively double-locks the resv -> if reserve_shared fails on the second episode (memory pressure), reverse-backoff releases only the first -> BO reservation lock held forever -> every later CS/validation touching that BO blocks (unkillable for intr=false callers). No memory-write primitive, no escalation path.",
  "evidence": "run.log + run.2.log (stock: first_lock=0 second_lock=0 (EALREADY would be -37), after unlocks ticket->acquired = 1 (correct value: 0...), RESULT=REPRODUCED); fix_run.log (patched drm.ko: first_lock=0 second_lock=-37, ticket->acquired = 0, RESULT=FIXED); fix_build.log (105-unit drm.ko rebuild with fix.diff, clean); build.log (proof module build); VERDICT.md source trace: ttm_execbuf_util.c:113/119-124, linux_wwmutex.c:62/109-120/188-202, kern_lock.c:125-143, list.h:187-189, linux_reservation.c:93-94, drm_modeset_lock.c:270-276 (second consumer already handling -EALREADY = dead code today), radeon_cs.c:103-104 + amdgpu_bo_list.c (no dedupe of duplicate handles)",
  "kernel_refs": [
    "sys/dev/drm/ttm/ttm_execbuf_util.c:113",
    "sys/dev/drm/ttm/ttm_execbuf_util.c:119-124",
    "sys/dev/drm/linux_wwmutex.c:62",
    "sys/dev/drm/linux_wwmutex.c:109-120",
    "sys/dev/drm/linux_wwmutex.c:188-202",
    "sys/kern/kern_lock.c:125-143",
    "sys/dev/drm/drm_modeset_lock.c:270-276",
    "sys/dev/drm/linux_reservation.c:69-99",
    "sys/dev/drm/radeon/radeon_cs.c:103-104",
    "sys/dev/drm/amd/amdgpu/amdgpu_bo_list.c:52-100"
  ],
  "poc_changes": "authored from scratch (no seed): KLD performing ttm_eus exact duplicate-BO lock/unlock sequence against the live drm.ko ww_mutex implementation; module made fix-aware (second unlock only when second lock returned 0, mirroring the dups separation) so the same binary validates baseline and patched kernels; build needed the drm compat KCFLAGS include set (sys/dev/drm/drm/Makefile:121-126) and sys/conf.h for DEV_MODULE",
  "attempts": 4,
  "guest_uname": "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",
  "runtime_sec": 900,
  "guest_dirty": 0,
  "build_cmd": "kldload drm; make SYSDIR=/usr/src/sys   (in findings/poc/DF-5138/ on the guest)",
  "run_cmd": "kldload ./df5138_wwproof.ko; sleep 1; dmesg | grep DF5138; kldunload df5138_wwproof",
  "code_hash": "50a269ad32899eb83e7c3caf0265df95139bd8d7e6d4606f37924ba5e811186e",
  "notes": "Guest dirty-then-cleaned: patched /usr/src + rebuilt drm.ko/radeon.ko during validation, restored /boot/kernel/drm.ko, final vm.sh reset with-src. Environment issue hit and documented (unload_hang.txt): kldunload drm of the STOCK module hangs (D-state holding kld lock) requiring one clean reboot mid-validation. Cross-file note: the root-cause line change is in linux_wwmutex.c (its own audit row should cross-reference DF-5138); the in-file alternative fix (O(n^2) dedupe in ttm_eu_reserve_buffers) was rejected as strictly worse.",
  "recommended_fix": "In __wwlock() (sys/dev/drm/linux_wwmutex.c) return -EALREADY when the lock is already held by the calling acquire context: if (ctx != NULL && ww->ctx == ctx) return (-EALREADY); \u2014 restoring the Linux ww_mutex contract that ttm_execbuf_util.c:119-124 and drm_modeset_lock.c:270-276 already code against",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 x86_64 (same kernel; single-module drm.ko rebuild)",
  "fix_verdict": "fix.diff applied to guest /usr/src/sys/dev/drm/linux_wwmutex.c, drm.ko rebuilt (fix_build.log: 105 compile units, clean -Werror build, 20s), installed to /boot/kernel, guest rebooted, proof module re-run: second_lock=-37 (-EALREADY as on Linux), ticket->acquired=0 after unlocks, RESULT=FIXED (fix_run.log). Baseline (stock drm.ko) had second_lock=0 / acquired=1 / RESULT=REPRODUCED. git apply --check clean against the unmodified tree.",
  "fix_evidence": "fix_run.log (DF5138-PROOF: RESULT=FIXED (-EALREADY returned; dup separated; accounting correct)); fix_build.log (full patched drm.ko build); run.log (baseline RESULT=REPRODUCED); unload_hang.txt (stock kldunload drm environment hang documented, unrelated to fix)"
}