โฌข DragonFlyBSD Kernel Audit
DF-2929 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-2929",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced on the stock INVARIANTS guest: the in-tree DRM wrapper hrtimer_start_range_ns() (sys/dev/drm/linux_hrtimer.c:88-119), when called on an already-armed timer โ€” exactly what intel_uncore.c:___force_wake_auto() does on every auto-forcewake i915 register access โ€” re-runs systimer_init_oneshot() on a SYSTF_ONQUEUE-linked timer; the bzero() at sys/kern/kern_systimer.c:363 erases the node links while neighbours still point at it and also clears the ONQUEUE flag that would make systimer_add()'s KKASSERT (:148) catch it, so the queue silently gains ghost links/cycles and the dispatcher then unlinks through stale pointers: panic 'Bad link elm ... prev->next != elm' in systimer_intr() (via pcpu_timer_process_oncpu/splz_timer) and in systimer_del(). Raw engine sequence (harness mode 2) shows the corruption structurally (st<->P cycle, 8 revisits in 24 hops). On production kernels the same unlink is a wild write through a stale tqe_prev plus double-dispatch of one-shot callbacks. fix.diff (wrapper deletes the pending systimer on its owning cpu before re-init) validated: 8/8 clean runs, no panic, one fire per cycle.",
  "exploit_chain": "hrtimer_start_range_ns on armed timer -> bzero of queued struct systimer -> ghost links + cycle in gd_systimerq -> systimer_intr/TAILQ_REMOVE through stale tqe_prev (wild write; INVARIANTS catches as 'Bad link elm') -> panic / clock corruption. Production-kernel escalation beyond DoS additionally requires controlling the reused node contents (node lives in driver-owned memory) โ€” not demonstrated, recorded as hardening blocker.",
  "evidence": [
    "run.log: 'DF2929 mode=2: forward hops=24 seen_st=8 seen_p=8 ... => QUEUE CORRUPT'",
    "panic.txt: panic 'Bad link elm' traces through systimer_del+0xd4 (mode 2) and systimer_intr+0x235 <- pcpu_timer_process_oncpu+0x82 <- splz_timer+0x13 cpuid=4 (mode 5, real drm.ko path)",
    "run.fix.log: 8x 'queue ok' + dht_fires=+1/run, guest stays up with patched drm.ko",
    "VERDICT.md: line-accurate root cause and the killed cross-cpu-del reachability hypothesis"
  ],
  "kernel_refs": [
    "sys/kern/kern_systimer.c:363",
    "sys/kern/kern_systimer.c:148",
    "sys/kern/kern_systimer.c:359-371",
    "sys/dev/drm/linux_hrtimer.c:88-119",
    "sys/dev/drm/i915/intel_uncore.c:74-78",
    "sys/dev/drm/i915/intel_uncore.c:1165-1176"
  ],
  "poc_changes": "No prior seed โ€” pack authored fresh. PoC iterated during verification: (1) unprivileged ppoll/nanosleep precise-sleep racer (reachability probe; hypothesis killed: wakeups are cpu-local, 1.2M iterations zero asserts โ€” kept as run.2.log/df2929_ppollrace.c); (2) KLD harness modes 1/2 (raw engine sequence, structural corruption detector); (3) mode 5 driving the real in-tree hrtimer_start_range_ns via MODULE_DEPEND(drm). Removed an initial lwkt_setcpu_self() from the sysctl handler that caused an unrelated lockmgr artifact-panic.",
  "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": 3300,
  "guest_dirty": 0,
  "build_cmd": "cd /root/df2929 && make  (KMOD df2929, COPTS+=-I/usr/src/sys/dev/drm/include) && kldload ./df2929.ko",
  "run_cmd": "sysctl kern.df2929_run=5",
  "code_hash": "9dc75f3cb9c8d538341cd8e90b31a0d5701ae4ba5c62ff2a455ac40c236c6496",
  "notes": "Guest: 6 vCPU KVM, INVARIANTS kernel #0 (X86_64_GENERIC), no i915 hardware โ€” wrapper driven directly by the KLD (root-loaded; the executed kernel path hrtimer_start_range_ns->systimer_init_oneshot->systimer_add is the production path, driven unprivileged-ly via GPU ioctls on i915 hardware). QMD queue checks make INVARIANTS kernels panic; production kernels perform the wild unlink silently. Related: DF-0154 (KKASSERT-only invariants) โ€” this finding supplies the concrete in-tree violator pass 1 said did not exist.",
  "recommended_fix": "linux_hrtimer.c: hrtimer_start_range_ns() must delete the pending systimer on its owning cpu (mirror hrtimer_cancel()'s lwkt_setcpu_self dance) before systimer_init_oneshot(); see fix.diff",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (kernel unchanged; drm.ko rebuilt from patched /usr/src/sys/dev/drm/linux_hrtimer.c)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Applied fix.diff to guest /usr/src/sys/dev/drm/linux_hrtimer.c, rebuilt drm.ko (make obj && make in sys/dev/drm/drm), installed, rebooted, re-ran the identical PoC (kern.df2929_run=5) 8 times: queue integrity 'ok' every run, dht_fires increments by exactly 1 per run, no panic, guest stays up. Baseline with stock drm.ko panicked in systimer_intr on the first run. Bad behavior gone.",
  "fix_evidence": [
    "run.fix.log: 8x 'DF2929 mode=5: ... => queue ok' + 'cancelled, dht_fires=1..4' + uptime output",
    "env.txt: running drm.ko MD5 == patched build MD5 (52965c2c7a18026a7d9089264ab796b7)",
    "fix.diff: git-apply-able unified diff against sys/dev/drm/linux_hrtimer.c"
  ]
}