DF-3645 / verdict.json
{ "finding_id": "DF-3645", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-certain lifetime bug, radeon/ci twin of DF-3588 (amdgpu), DF-3611 (radeon si), DF-3624 (amdgpu ci). Three legs: (1) ci_parse_power_tables error path (ci_dpm.c:5669-5673) kfrees rdev->pm.dpm.ps and every ps_priv but leaves rdev->pm.dpm.ps dangling and num_ps at its parsed value. The path is reachable when the per-state kzalloc fails (5624-5628) or when the rdev->pm.power_state[i].clock_info guard (5620-5623) trips - which it does whenever radeon_atombios_parse_power_table_4_5 bailed out early leaving later entries NULL, and the guard itself reads rdev->pm.power_state[i] out of bounds when the legacy parser fell back to its single default power_state (radeon_atombios.c:2784) while ci iterates the crafted state_array->ucNumEntries. (2) ci_dpm_init error paths (5764/5770/5776/5782) kfree rdev->pm.dpm.priv without NULLing it. (3) radeon_pm_init_dpm (radeon_pm.c:1448) calls radeon_dpm_init(rdev) and DISCARDS the return, then immediately runs radeon_dpm_setup_asic() -> ci_dpm_setup_asic -> ci_read_clock_registers (ci_dpm.c:1904-1929), which WRITES 14 register values into the freed ci_power_info, and radeon_dpm_enable -> ci_dpm_enable which reads pi->voltage_control from freed memory. At teardown, radeon_driver_unload_kms -> radeon_device_fini -> cik_fini (cik.c:8730) -> radeon_pm_fini -> radeon_pm_fini_dpm -> ci_dpm_fini (ci_dpm.c:5703-5714) unconditionally kfrees the already-freed ps_priv array, ps array and priv -> double free. Kernel-level reproduction is impossible on this virtio-only guest; verdict inconclusive with source-level certainty.", "exploit_chain": "Force a ci_dpm_init failure with rdev->pm.dpm state already populated (memory-pressure kzalloc failure inside ci_parse_power_table, or a crafted VBIOS whose legacy power-state parse leaves power_state[i].clock_info NULL for some i < ucNumEntries): boot then writes into freed ci_power_info (UAF write of hardware register values) and unloading the driver double-frees the ps array - classic exploitable double-free on the DRM slab if the intervening UAF writes havent already crashed. Requires the same crafted-VBIOS/allocation-pressure preconditions as the si twin DF-3611; no CI hardware on this guest so no kernel-path PoC was possible.", "evidence": "VERDICT.md (full source trace); env.txt (guest has no AMD GPU: pciconf 0x1002 count=0, /dev/dri absent, no drm kld); fix.diff", "kernel_refs": [ "sys/dev/drm/radeon/ci_dpm.c:5608", "sys/dev/drm/radeon/ci_dpm.c:5620", "sys/dev/drm/radeon/ci_dpm.c:5624", "sys/dev/drm/radeon/ci_dpm.c:5669", "sys/dev/drm/radeon/ci_dpm.c:5703", "sys/dev/drm/radeon/ci_dpm.c:5764", "sys/dev/drm/radeon/ci_dpm.c:5782", "sys/dev/drm/radeon/ci_dpm.c:5835", "sys/dev/drm/radeon/radeon_pm.c:1448", "sys/dev/drm/radeon/radeon_pm.c:1458", "sys/dev/drm/radeon/ci_dpm.c:1904", "sys/dev/drm/radeon/cik.c:8730", "sys/dev/drm/radeon/radeon_atombios.c:2784" ], "poc_changes": "n/a \u2014 no seeded PoC; source-trace only", "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": 45, "guest_dirty": 0, "build_cmd": null, "run_cmd": null, "code_hash": null, "notes": "pass 2 radeon/ci_dpm.c; twins: DF-3588/DF-3611/DF-3624. Guest virtio-only per task; harness scratch files under /root removed.", "recommended_fix": "NULL out rdev->pm.dpm.{ps,priv} and reset num_ps on every free site in ci_dpm.c, and honor the radeon_dpm_init() return value in radeon_pm_init_dpm().", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix validation would require a CI-class GPU with a crafted VBIOS (baseline panic/corruption) and a single-fix kernel build; the audit guest is virtio-only, so the fix is source-reviewed only (fix.diff).", "fix_evidence": "fix.diff in this pack; not applied to sys/ (read-only audit target)." } |