# DF-5177 — `hrtimer_cancel()` returns while the timer callback is still running (caller frees → deterministic use-after-free write)

## What

`sys/dev/drm/linux_hrtimer.c:121-150`: `hrtimer_cancel()` snapshots
`tq`/`running` under `timer->timer_token` but **releases the token before
doing any of the work** (linux_hrtimer.c:133). A concurrent
`hrtimer_start()` — which the Linux hrtimer API allows from any CPU while
the callback is in flight (i915_pmu, intel_uncore, i915_perf all do this) —
then, still under the token (linux_hrtimer.c:109-118):

- falsifies `timer->running = false` (linux_hrtimer.c:112) — the callback
  *is* running on another CPU's taskqueue thread,
- reassigns `timer->gd` (linux_hrtimer.c:114) and re-arms the systimer on
  the new CPU.

`hrtimer_cancel()` consequently cancels/drains `taskqueue_thread[gd]` of the
**new** CPU (linux_hrtimer.c:128) instead of the queue whose thread is
actually executing `__hrtimer_task`, finds the task neither pending nor
running there, and **returns while `timer->function` is still executing**.
Linux guarantees `hrtimer_cancel()` excludes against a running callback;
callers therefore free the timer right after (e.g. `i915_pmu_unregister`,
i915_pmu.c:1089), and the epilogue of `__hrtimer_task`
(linux_hrtimer.c:58-68) then operates on freed memory:
`lwkt_gettoken(&timer->timer_token)`, `timer->running = false`,
`timer->active = false`, and on the RESTART path a full
`systimer_init_oneshot(&timer->st, ...)` — a bzero + insertion of the freed
systimer into a live per-CPU queue.

## Reproduce (on the audit QEMU guest)

```
scp df5177.c Makefile dfbsd:/root/df5177/
ssh dfbsd 'cd /root/df5177 && make SYSDIR=/usr/src/sys && kldload drm'
ssh dfbsd 'kldload /root/df5177/df5177.ko; dmesg | tail'
```

## Expected (baseline, unpatched drm.ko)

```
[df5177] hrtimer_cancel() returned ret=1, cb_done=0 (Linux API requires cb_done==1 here!)
[df5177] timer freed and reallocated: victim=0xfffff8008bb481f0 same=1; ...
[df5177] UAF-WRITE victim[152]=0x00 (was 0x5A: epilogue cleared active)
[df5177] UAF-WRITE victim[153]=0x00 (was 0x5A: epilogue cleared running)
[df5177] EXPLOIT-PRIMITIVE PROVEN: cancel returned before callback + 2 post-free writes into a reallocated object
```

The freed chunk is immediately re-allocated at the same address and the
in-flight epilogue writes into it — a deterministic cross-object
use-after-free write. (Guest stays up; the corruption is contained only
because the PoC re-initializes the victim's token slot and the callback
returns NORESTART — see VERDICT.md for the RESTART-path escalation.)

(fix-validated: with `fix.diff`, the same PoC shows
`hrtimer_cancel() returned ret=1, cb_done=1` and `changed=0` — see
`run.fix.log`.)

## Files

- `df5177.c` / `Makefile` — the PoC KLD
- `build.log` / `run.log` — baseline evidence (deterministic UAF write)
- `run.fix.log` — fixed-kernel rerun (no early return, no writes)
- `fix.diff` — one coherent fix for DF-5176 + DF-5177
- `panic.serial.txt` — an early PoC-harness crash (DECLARE_MODULE misuse),
  kept for transparency; not a kernel finding
- `env.txt`, `VERDICT.md`, `manifest.json`, `verdict.json`
