⬢ DragonFlyBSD Kernel Audit
DF-4494 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4494",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced at logic level with the verbatim replica (harness modes 4494/4494u/4494w): the ENOMEM path of rs780_parse_power_table kfrees rdev->pm.dpm.ps without NULLing it (:828-832) while boot_ps already points into the array, and radeon_pm.c:1448 discards the error \u2014 teardown rs780_dpm_fini (:959) kfrees the dangling pointer => DOUBLE FREE of the sizeof(radeon_ps)*ucNumStates allocation (canary: DOUBLE FREE of 0x8004d1d00 (size 96) DETECTED, exit=98), and the continued probes rs780_dpm_enable (:608->:78->:82) reads ps[0].ps_priv from the freed array then derefs the 0xB0 free-fill => SIGBUS/UAF read; separately, the missing-IntegratedSystemInfo exit of rs780_dpm_init calls radeon_dpm_fini itself (:933) and returns -EINVAL that radeon_pm.c:1448 discards, so rs780_get_pm_mode_parameters (:57-58) WRITES pi->crtc_id/refresh_rate into the freed igp_power_info (canary: 8 bytes != 0xb0 -> object WRITTEN AFTER FREE, then SIGBUS). The ENOMEM arm needs no crafted VBIOS (memory pressure at the per-state kzalloc on stock firmware), same PR:L reasoning as family row DF-4188 (sumo twin; also DF-4125 rv6xx / DF-4144 trinity / DF-4117 cypress / DF-3973 btc). Kernel-level repro hardware-gated (no RS780 IGP in the QEMU guest). Fix proven: cleanup NULLs dpm.ps/boot_ps/uvd_ps, init no longer self-finalizes, enable guards NULL boot_ps; radeon.ko builds under -Werror (MAKE_RC=0).",
  "exploit_chain": "memory pressure (or crafted table) at rs780_parse_power_table state kzalloc -> dpm.ps freed but dangling + boot_ps pointing into it -> radeon_pm.c:1448 swallows the error -> probe continues: UAF read of freed ps[0].ps_priv in rs780_dpm_enable (freed-slot contents become the igp_ps pointer dereferenced at :82) -> module unload: rs780_dpm_fini double-frees the array into the DRM slab; variant: missing igp_info header -> init self-finalizes -> continued probe performs a deterministic write-after-free into the freed igp_power_info slot (groomable small-object slab) before faulting",
  "evidence": "run.log ===== mode 4494 =====: DOUBLE FREE of 0x8004d1d00 (size 96) DETECTED, exit=98; ===== mode 4494u =====: boot_ps ... points into a FREED allocation -> USE AFTER FREE then SIGNAL 10, exit=99; ===== mode 4494w =====: freed object igp_power_info ... has 8 bytes != 0xb0 -> object WRITTEN AFTER FREE (UAF WRITE) then SIGNAL 10; all fixed modes exit=0 with canary_violations=0; radeon_module_build.log MAKE_RC=0 under -Werror",
  "kernel_refs": [
    "sys/dev/drm/radeon/rs780_dpm.c:830",
    "sys/dev/drm/radeon/rs780_dpm.c:933",
    "sys/dev/drm/radeon/rs780_dpm.c:959",
    "sys/dev/drm/radeon/rs780_dpm.c:57",
    "sys/dev/drm/radeon/rs780_dpm.c:608",
    "sys/dev/drm/radeon/radeon_pm.c:1448",
    "sys/dev/drm/radeon/radeon_pm.c:1705"
  ],
  "poc_changes": "wrote harness from scratch (no seed); canary allocator extended with 0xB0 free-fill + freed-object write detection so the UAF write is observable; -fno-delete-null-pointer-checks for kernel trap semantics",
  "attempts": 2,
  "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": 300,
  "guest_dirty": 0,
  "build_cmd": "./build.sh (on audit guest, cc 8.3, -fno-delete-null-pointer-checks)",
  "run_cmd": "./run.sh  (modes 4494 4494u 4494w; full matrix in run.log)",
  "code_hash": "3b7698d1a8d20944dd3c2ea4c0010146536ed001ff5cf1034c4c4ac650591741",
  "notes": "known finding DF-1650 excluded (pass-1). The third inconsistent exit (crev>=3 at :914-916 returns -EINVAL with no cleanup) is memory-safe only by accident and is covered by the same fix. Impact panic per family precedent (DF-4188); the underlying class is memcorrupt (double free + write-after-free into groomable DRM slabs).",
  "recommended_fix": "NULL dpm.ps/boot_ps/uvd_ps after freeing on every parse error path (freeing prior ps_priv entries), and stop rs780_dpm_init from calling radeon_dpm_fini on its own error path (teardown owns the single free), plus a NULL-boot_ps guard in rs780_dpm_enable \u2014 as in fix.diff",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "harness_fixed 4494: dpm.ps is NULL after the failed parse, fini clean, exit=0; 4494u: enable guard fires, exit=0; 4494w: init no longer self-finalizes (canary_violations = 0), exit=0; combined fix builds radeon.ko under -Werror (MAKE_RC=0)",
  "fix_evidence": "run.log fixed sections for modes 4494/4494u/4494w (all exit=0); radeon_fix_apply.log hunks 1, 7, 8; radeon_module_build.log MAKE_RC=0"
}