# DF-2929 — systimer re-init of a queued timer (via DRM hrtimer wrapper) corrupts gd_systimerq

## What

`sys/kern/kern_systimer.c:systimer_init_oneshot()` (line 359-371) re-initializes
a `struct systimer` with an unconditional `bzero()` at line 363. If the timer is
still armed (`SYSTF_ONQUEUE`, linked into the owning cpu's `gd_systimerq`), the
bzero erases its `node.tqe_next`/`node.tqe_prev` links **while the neighbours
and possibly the queue head still point at it**, and also clears the very
`SYSTF_ONQUEUE` flag that `systimer_add()`'s KKASSERT at line 148 checks — so
the subsequent re-add is **silent even on INVARIANTS kernels**. The queue is
left with ghost links / cycles; the dispatcher (`systimer_intr`) and
`systimer_del()` then unlink through stale pointers.

The in-tree trigger is the DRM Linux-compat wrapper
`sys/dev/drm/linux_hrtimer.c:hrtimer_start_range_ns()` (line 88-119): it
implements Linux `hrtimer_start()` semantics (re-arm an armed timer is legal
Linux API) by calling `systimer_init_oneshot()` on a possibly-armed timer with
**no dequeue first**. `intel_uncore.c:___force_wake_auto()` (line 1165-1176)
calls `fw_domain_arm_timer()` → `hrtimer_start_range_ns()` on **every**
auto-forcewake register access, so any two i915 register accesses within the
1 ms timer window execute the corrupting sequence (routinely, from unprivileged
GPU ioctls).

## Impact

Kernel memory corruption of the per-cpu timer queue (the queue that drives
hardclock/statclock): ghost links, cycles, orphaned nodes; double-dispatch of
one-shot timers; wild `TAILQ_REMOVE` writes through stale `tqe_prev` pointers
on production kernels; demonstrated kernel panics in `systimer_intr()` and
`systimer_del()` on the INVARIANTS guest.

## Reproduce (on the QEMU guest)

```
# build the KLD harness (as root; it MODULE_DEPENDs on drm.ko)
cd /root/df2929 && make

# control: arm P/st/N one-shots, verify queue, clean up   -> "queue ok"
sysctl kern.df2929_run=1

# raw engine sequence (bzero of queued timer + re-add):   -> "QUEUE CORRUPT"
sysctl kern.df2929_run=2      # + panic "Bad link elm" in systimer_del()

# REAL in-tree wrapper path (hrtimer_start_range_ns twice): -> panic in
# systimer_intr() via pcpu_timer_process_oncpu/splz_timer
sysctl kern.df2929_run=5
```

## Fix

`fix.diff` — make `hrtimer_start_range_ns()` delete the pending systimer
(on its owning cpu, mirroring `hrtimer_cancel()`) before re-initializing.
Validated: rebuild drm.ko with the fix → same PoC runs clean ×8, no panic,
queue integrity ok, exactly one fire per cycle.
