⬢ DragonFlyBSD Kernel Audit
DF-4145 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4145",
  "status": "reproduced",
  "verdict": "REPRODUCED with the findings MECHANISM CORRECTED by the rig. Confirmed exactly as claimed: a pplib state with ucNumDPMLevels==0 parses with num_levels==0 (trinity_parse_pplib_clock_info:1726 never runs for it); when that state is selected, the verbatim trinity_dpm_pre_set_power_state -> trinity_apply_state_adjust_rules executes :1603-:1618 (proved by Dpm0PgNbPsLo 1->2 / DpmXNbPsLo 2->2 overwritten at :1615/:1617) and then the :1619 store ps->levels[ps->num_levels - 1].allow_gnb_slow = 0 executes OUT OF BOUNDS. Corrected mechanism: num_levels is u32, so the index is 0xFFFFFFFF and the store addresses ps + 4 + 0xFFFFFFFF*12 + 7 = ps + 0xBFFFFFFFF -- a WILD 1-byte-zero store ~48 GiB past the object, NOT the claimed ps-1 window write into the MSB of pi->requested_rps.ps_priv (C unsigned arithmetic; gdb computes the exact fault address 0x140047b1af == ps + 0xBFFFFFFFF, and a MAP_FIXED canary page at that exact offset receives precisely one changed byte, 0xaa->0x00, of 12288). The read side (trinity_dpm_get_sclk:2102 levels[num_levels-1].sclk) addresses ps + 0xBFFFFFFF8 -- same wild region (rig get_sclk returns the canary bytes), not an intra-object garbage read. In-kernel consequence: page fault -> panic at the first state transition (certain floor), or a silent 1-byte zeroing of unrelated kernel memory if the +48GiB offset happens to be mapped; NO pointer-surgery capability (constant offset, constant 0x00 value) -- the findings non-canonical-ps_priv chain is refuted. fix.diff (reject zero-level states at parse + num_levels guard on the store) A/B-validated: fixed build rejects the crafted table at init (-22, power state 1 has no dpm levels), the write is unreachable, good tables unaffected.",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "exploit_chain": "Crafted VBIOS: IntegratedSystemInfo v1_7 with ucNBDPMEnable=1 (verbatim-parsed by trinity_parse_sys_info_table, enable_nbps_policy defaults true) + a pplib state carrying an SDSTATE/HDSTATE/UI_BATTERY classification and ucNumDPMLevels=0 -> parse leaves that states num_levels==0 -> state selection (radeon_pm machinery) -> trinity_dpm_pre_set_power_state -> trinity_apply_state_adjust_rules:1619 -> wild byte-zero store at pi->requested_ps + 0xBFFFFFFFF and OOB read at +0xBFFFFFFF8 (get_sclk feeding watermark programming) -> kernel page fault -> panic, or 1-byte zero of unrelated kernel VA where mapped. No unprivileged-userland trigger; attacker data is the GPU ROM.",
  "evidence": "run.log (4145-guard: FAULT addr=0x14004bdfff == ps+0xBFFFFFFFF with the rig printing the computed wild offset; 4145-fullflow: canary byte 0xaa->0x00 written by the verbatim store, 1/12288 bytes changed, Dpm0/DpmX reachability proof, get_sclk returning canary bytes; FIXED: parse rejected rc=-22), run.2.log, build.log (68x identical + DF-4145 fix lines == fix.diff additions (11 lines)), rig/, VERDICT.md (full corrected-arithmetic derivation), env.txt",
  "kernel_refs": [
    "sys/dev/drm/radeon/trinity_dpm.c:1619",
    "sys/dev/drm/radeon/trinity_dpm.c:1726",
    "sys/dev/drm/radeon/trinity_dpm.c:1603",
    "sys/dev/drm/radeon/trinity_dpm.c:2102",
    "sys/dev/drm/radeon/trinity_dpm.c:974",
    "sys/dev/drm/radeon/trinity_dpm.c:988"
  ],
  "poc_changes": "Seed pack had no runnable PoC. Built the verbatim rig from scratch. Two rig-driven corrections vs the seed claim set: (1) the :1619 store target is ps+0xBFFFFFFFF (u32 index wrap x sizeof(trinity_pl)=12), not ps-1 -- proven by fault-address arithmetic, gdb inspection and a MAP_FIXED canary page catching exactly one byte; (2) the levels[num_levels-1].sclk reads are the SAME wild offset, not intra-object garbage. The trigger/reachability/OOB-write class of the finding stand unchanged.",
  "attempts": 3,
  "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": 150,
  "guest_dirty": 0,
  "build_cmd": "cd rig && sh ./build.sh /usr/src DF-4145",
  "run_cmd": "cd rig && sh ./run.sh",
  "code_hash": "trinity_rig.c a68eb1438bc82fb79063491af6293aeddf208ec2cf4f46f0c6040a1aed69e1a3; trinity_slice_verbatim.c 6afb17099769a0c4c9cd500356a757a12cf22d8e9bd67a710b452236019ba7c6",
  "notes": "Mechanism correction is the material delta of this verification: severity class (attacker-reachable kernel OOB write -> panic floor) is unchanged, but the claimed precise pointer-corruption primitive (ps-1 -> ps_priv MSB -> non-canonical deref) does not exist; exploitability ceiling is panic/DoS plus a speculative 1-byte zero at a fixed wild offset. Adjacent same-pattern sites confirmed in the slice: trinity_set_uvd_clock_before/after_set_eng_clock (:974/:988) read levels[num_levels-1].sclk at the same wild offset. Guest left up and clean.",
  "recommended_fix": "Reject pplib states whose idx loop yields k==0 (full unwind, -EINVAL) in trinity_parse_power_table and guard the :1619 store with ps->num_levels > 0 -- fix.diff, A/B validated.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "A/B validated in the verbatim rig on the identical crafted image: baseline executes the wild store (guard-mode fault at ps+0xBFFFFFFFF; fullflow canary byte written) and the wild read; fixed build rejects the zero-level state at parse (init rc=-22, power state 1 has no dpm levels) so :1619 is unreachable, and the defense-in-depth num_levels guard covers any other zero-level path. Good-table regression unchanged. Kernel-rebuild validation not performed (AMD-only path, guest has no AMD GPU); validated in the rig per the established protocol.",
  "fix_evidence": "run.log FIXED sections (RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site); build.log (DF-4145 fix lines == fix.diff additions (11 lines)); fix.diff"
}