⬢ DragonFlyBSD Kernel Audit
DF-4163 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4163",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "Structural UAF proven at source level, trigger timing-dependent and hardware-gated (inconclusive per family convention). radeon_pm_fini_dpm (radeon_pm.c:1690-1719) frees the DPM state via radeon_dpm_fini (:1705; per-asic finis kfree ps/ps_priv[]/priv without NULLing and leave num_ps>0, e.g. si_dpm.c:7078-7094) but never calls cancel_work_sync(&rdev->pm.dpm.thermal.work) and never clears rdev->pm.dpm_enabled. The thermal IRQ outlives pm_fini in every asic fini order (si.c:6945 pm_fini before :6950 irq teardown; cik.c:8731 vs :8736/:8742) and its IH handler schedules the work whenever dpm_enabled is set (si.c:6429-6430, r600.c:4343, evergreen.c:4902, cik.c:8125). A work item queued across the unload window therefore runs after the frees: the handlers entry check (:881) and the under-mutex recheck in radeon_dpm_change_power_state_locked (:1062) both read the stale-true flag (suspend DOES clear it at :1263 -- verified, suspend path is safe; fini is the hole), and radeon_dpm_fini runs outside pm.mutex so nothing orders the handler against the frees. Consumption: radeon_dpm_pick_power_state walks rdev->pm.dpm.ps[i].class over freed memory (:945-947), the dangling ps becomes requested_ps, and radeon_dpm_set_power_state (:1154) feeds freed ps_priv/priv into the asic SMC programming. Guest: virtio VGA only, zero 0x1002 devices (env.txt) -> not runtime-testable. Killed adjacent candidates: radeon_hwmon_fini sensor kfree is SAFE (sensor task thread holds its lock across st->func, kern_sensors.c:301-344, and sensor_task_unregister takes that lock, :238-250 -- no in-flight-after-unregister window); suspend-side thermal race closed by :1263.",
  "exploit_chain": "On radeon hardware: root-initiated driver unload (kldunload/detach) racing a thermal threshold IRQ -> UAF read of freed kzalloc memory driving GPU SMC clock/voltage programming; realistic outcome panic/garbage-DPM, escalation improbable (root already required to unload).",
  "evidence": "VERDICT.md (liveness/ordering proof + killed candidates); env.txt (hardware absence + md5 match); fix.diff (cancel_work_sync + dpm_enabled=false at fini head; composed against DF-4162-patched tree, apply DF-4162 first)",
  "kernel_refs": [
    "sys/dev/drm/radeon/radeon_pm.c:1690",
    "sys/dev/drm/radeon/radeon_pm.c:1705",
    "sys/dev/drm/radeon/radeon_pm.c:881",
    "sys/dev/drm/radeon/radeon_pm.c:1062",
    "sys/dev/drm/radeon/radeon_pm.c:945",
    "sys/dev/drm/radeon/si.c:6429",
    "sys/dev/drm/radeon/si.c:6945",
    "sys/dev/drm/radeon/si_dpm.c:7078"
  ],
  "poc_changes": "No seed PoC; trigger is a hardware unload+thermal race documented in README.md.",
  "attempts": 1,
  "guest_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",
  "runtime_sec": 15,
  "guest_dirty": 0,
  "build_cmd": "n/a (hardware-gated)",
  "run_cmd": "n/a (hardware-gated)",
  "code_hash": "",
  "notes": "Pass-2 hunt item (c) therm work handlers vs modeset/teardown. Upstream-inherited shape (upstream fini_dpm likewise lacks the cancel), but real in this tree; severity Low per rubric (unload requires root). fix.diff composes with DF-4162s (verified via /tmp textual compose).",
  "recommended_fix": "cancel_work_sync(&rdev->pm.dpm.thermal.work) + rdev->pm.dpm_enabled = false at the top of radeon_pm_fini_dpm before any DPM state is freed (see fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff closes the fini window (flush + flag-clear before frees; stragglers exit at the :881/:1062 checks); cannot be runtime-validated without radeon hardware.",
  "fix_evidence": "findings/poc/DF-4163/fix.diff"
}