{
  "finding_id": "DF-5177",
  "status": "reproduced",
  "reproduced": true,
  "impact": "kernel use-after-free write (memory corruption), deterministic: hrtimer_cancel() returns while timer->function is still executing (Linux requires exclusion), so the callers kfree() is followed by __hrtimer_task epilogue writes (lwkt token ops + running/active byte clears) into freed-then-reallocated memory; same interleave with a RESTART-returning callback reaches systimer_init_oneshot() on the freed chunk (bzero + insertion into a live per-cpu systimer queue, ending in a timer->function call from attacker-reclaimed memory); uid=0 chain not completed (drm hrtimer callers are GPU-gated on this guest)",
  "confidence": "certain",
  "verdict": "hrtimer_cancel() (linux_hrtimer.c:121-150) snapshots tq/running under timer_token and then RELEASES the token before systimer_del/taskqueue_cancel/taskqueue_drain. A concurrent hrtimer_start() \u2014 legal from any cpu while the callback runs, and required to work by the Linux API (i915_pmu.c:130-135 start vs i915_pmu.c:1089 cancel; intel_uncore.c:70-78 arm vs :393 cancel outside uncore.lock) \u2014 sets running=false (a lie, linux_hrtimer.c:112) and moves timer->gd (:114), so cancel() drains the NEW cpus taskqueue (linux_hrtimer.c:128) instead of the one whose thread is executing __hrtimer_task, finds the task neither pending nor running there, and returns ret=1 with the callback still in flight. Reproduced deterministically on the guest with a sleep-orchestrated KLD (start on cpu0 -> fire -> callback sleeps 333ms -> start on cpu1 -> cancel on cpu2): hrtimer_cancel() returned ret=1, cb_done=0, then kfree + same-address realloc + poison shows the epilogue writing into the reallocated object (UAF-WRITE victim[152]=0x00 (was 0x5A: epilogue cleared active), UAF-WRITE victim[153]=0x00 ... running, changed=2), guest up. fix.diff (drop the running lie; loop cancel until active==false under the token; hold the token across systimer_del) rebuilt as drm.ko in the guest and re-run: cb_done=1 at cancel return and zero post-free writes. Distinct from known DF-2174 (epilogue active-clobber) and DF-2175 (re-init of armed systimer).",
  "exploit_chain": "unpriv GPU user triggers a legal cross-cpu hrtimer restart while a drm hrtimer callback is in flight (i915 pmu gt-unpark on cpu B during a sampling callback on cpu A; or intel_uncore forcewake re-arm racing forcewake_resets unlocked cancel loop) -> hrtimer_cancel returns early -> driver teardown frees the containing object (i915_pmu_unregister pattern) -> in-flight __hrtimer_task epilogue performs lwkt-token ops and flag writes on freed memory -> same-size reclaim (demonstrated same-address) places a victim object under the writes: cleared refcounts/flags give premature-free/state-confusion, and the RESTART variant inserts the freed systimer into a live per-cpu queue yielding a timer->function call from reclaimed memory. Deterministic primitive proven on QEMU; uid=0 not chained (organic trigger needs i915/amdgpu hardware, like DF-5169).",
  "evidence": "run.log: [df5177] hrtimer_cancel() returned ret=1, cb_done=0 (Linux API requires cb_done==1 here!) + timer freed and reallocated: victim=0xfffff8008bb481f0 same=1 + UAF-WRITE victim[152]=0x00 (was 0x5A: epilogue cleared active) + UAF-WRITE victim[153]=0x00 (was 0x5A: epilogue cleared running) + EXPLOIT-PRIMITIVE PROVEN ... 2 post-free writes; run.fix.log: cb_done=1 + victim[152]/[153] still 0x5A (no write) + changed=0; VERDICT.md sections 1-4 trace every cited line",
  "kernel_refs": [
    "sys/dev/drm/linux_hrtimer.c:121-150",
    "sys/dev/drm/linux_hrtimer.c:112",
    "sys/dev/drm/linux_hrtimer.c:114",
    "sys/dev/drm/linux_hrtimer.c:128",
    "sys/dev/drm/linux_hrtimer.c:133-145",
    "sys/dev/drm/linux_hrtimer.c:42-69",
    "sys/dev/drm/i915/i915_pmu.c:1089",
    "sys/dev/drm/i915/i915_pmu.c:130-135",
    "sys/dev/drm/i915/intel_uncore.c:393",
    "sys/dev/drm/i915/i915_perf.c:1952"
  ],
  "poc_changes": "PoC written from scratch (no seed). v1 migrated the kldload user thread via lwkt_setcpu_self (wedged the guest scheduler; two cpus spinning in acpi_hpet_early_get_timecount) and misused DFs DECLARE_MODULE by passing the handler instead of a moduledata_t variable (kernel strcmpd the handlers machine-code bytes as the module name -> GPF in module_lookupbyname; panic.serial.txt) \u2014 both harness bugs diagnosed via QEMU-monitor pmemsave + page-table walk + offline symbolization, then eliminated. v2 uses dedicated per-cpu kernel threads (lwkt_create(..., TDF_NOSTART, cpuid) + lwkt_schedule) and the moduledata_t pattern; the freed chunk is re-inited (token) + poisoned (0x5A state bytes) so the deterministic epilogue writes are observable without hanging or panicking, isolating the corruption primitive",
  "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": 2400,
  "guest_dirty": 1,
  "build_cmd": "scp df5177.c Makefile dfbsd:/root/df5177/ ; ssh dfbsd cd /root/df5177 && make SYSDIR=/usr/src/sys   ; fix phase: cd /usr/src && patch -p1 < /root/fix.diff && cd sys/dev/drm/drm && make SYSDIR=/usr/src/sys && cp drm.ko /boot/kernel && sync",
  "run_cmd": "ssh dfbsd kldload drm; kldload /root/df5177/df5177.ko; sleep 1; dmesg | tail -12",
  "code_hash": "15a44a020042203566a4a46101dbe602b0754442110a7ccd63f0c3f52d0bbd98",
  "notes": "Known pair DF-2174/DF-2175 NOT re-reported; this is the cancel-side non-atomicity + starts running-lie (early return -> caller free -> epilogue UAF). Adjacent weaker manifestations of the same root (wrong-queue taskqueue_cancel STAILQ walk-off NULL deref; unlocked double-read of timer->gd in the migrate branch) are described in the finding but subsumed by the fix. Guest reset (with-src) performed after validation.",
  "recommended_fix": "Make hrtimer_cancel atomic against hrtimer_start: drop the running=false write from hrtimer_start_range_ns, snapshot tq/gd under the token, keep the token held across systimer_del (including cpu migration), and loop {cancel+del+drain} until timer->active is false under the token (see fix.diff)",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff applied inside guest /usr/src (patch -p1 clean), drm.ko rebuilt from patched source, installed to /boot/kernel, guest rebooted, PoC rebuilt against the patched header and re-run: hrtimer_cancel() now returns only after the callback completed (cb_done=1), the freed chunk reallocation shows both poison bytes intact (still 0x5A (no write), changed=0) \u2014 the early-return UAF is eliminated while normal timer operation (start/fire/callback) is preserved",
  "fix_evidence": "run.fix.log ([df5177] hrtimer_cancel() returned ret=1, cb_done=1 ... victim[152] still 0x5A (no write) ... victim[153] still 0x5A (no write) ... changed=0 cancel_saw_cb_done=1) vs run.log (baseline: cb_done=0 + two UAF writes + EXPLOIT-PRIMITIVE PROVEN)"
}