β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4126

Inverted error checks at rv6xx_dpm_init:1957-1969 β€” ATOM-computed SPLL/MPLL reference divider used only when radeon_atom_get_clock_dividers FAILS; default used on success (rv6xx twin of DF-3976/DF-4119)

Summary

Both probes read 'if (ret) pi->..._ref_div = dividers.ref_div + 1; else default;' - inverted vs every sibling dispatch. On success the ATOM divider is discarded (default 4); on failure the callee-zeroed dividers yield ref_div+1 = 1 and rv6xx_output_stepping programs set_reference_divider(spll_ref_div-1) = 0 with a wrong fb_divider. Deterministic wrong-PLL programming, not an uninit read (callee memsets). Two-character invert fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4126 Β· 3 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
fix.diff β€” 758 B view raw

DF-4126 β€” Inverted error check at rv6xx_dpm_init:1957-1969 β€” ATOM-computed SPLL/MPLL reference divider used only when radeon_atom_get_clock_dividers FAILS, default used on success

  • File: sys/dev/drm/radeon/rv6xx_dpm.c
  • Lines: 1957-1969 Severity: Low Confidence: certain
  • Status: inconclusive (impact: none) β€” hardware-gated crafted-VBIOS/firmware threat model; source-level certainty, no GPU in guest.

Summary

Both clock-divider probes in rv6xx_dpm_init() test if (ret) where every sibling dispatch tests success: rv6xx_dpm.c:1957-1962 (SPLL): ret = radeon_atom_get_clock_dividers(..., &dividers); if (ret) pi->spll_ref_div = dividers.ref_div + 1; <- taken on FAILURE else pi->spll_ref_div = R600_REFERENCEDIVIDER_DFLT; rv6xx_dpm.c:1964-1969 (MPLL): same inverted shape for mpll_ref_div. On SUCCESS the ATOM-computed divider is discarded and the default (4) is used; on FAILURE the callee-zeroed dividers are consumed (radeon_atombios.c:2838 memsets dividers), yielding ref_div+1 = 1. The value is programmed into the PLL: r600_engine_clock_entry_set_reference_divider( step, pi->spll_ref_div - 1) at rv6xx_dpm.c:187-188 (programs 0 on the failure branch) and folds into the feedback divider at :184 β€” wrong SPLL/MPLL programming (display/GPU instability) from VBIOS-dependent atom behavior. rv6xx instance of the DF-3976/DF-4119 family (last member).

Reproduce

No runnable kernel PoC: requires RV6xx/i915 hardware. See VERDICT.md for the full line-by-line source trace and fix.diff for the validated patch.

Guest: 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

VERDICT.md
↓ download raw

DF-4126 verdict β€” Inverted error check at rv6xx_dpm_init:1957-1969 β€” ATOM-computed SPLL/MPLL reference divider used only when radeon_atom_get_clock_dividers FAILS, default used on success

Reproduced? β€” inconclusive (none)

Source-certain logic inversion (every sibling dispatch uses if (!ret); cypress DF-4119 and btc DF-3976 are the same line-shape). Hardware-gated, no observable impact without RV6xx silicon; the deterministic consequences are: success path ignores ATOM dividers (uses 4), failure path consumes the callee-zeroed struct and programs ref divider 0. Not memory-unsafe β€” radeon_atombios.c:2838 memsets the struct, so unlike the cypress wording there is no uninitialized read here.

Root cause (path:line)

Both clock-divider probes in rv6xx_dpm_init() test if (ret) where every sibling dispatch tests success: rv6xx_dpm.c:1957-1962 (SPLL): ret = radeon_atom_get_clock_dividers(..., &dividers); if (ret) pi->spll_ref_div = dividers.ref_div + 1; <- taken on FAILURE else pi->spll_ref_div = R600_REFERENCEDIVIDER_DFLT; rv6xx_dpm.c:1964-1969 (MPLL): same inverted shape for mpll_ref_div. On SUCCESS the ATOM-computed divider is discarded and the default (4) is used; on FAILURE the callee-zeroed dividers are consumed (radeon_atombios.c:2838 memsets dividers), yielding ref_div+1 = 1. The value is programmed into the PLL: r600_engine_clock_entry_set_reference_divider( step, pi->spll_ref_div - 1) at rv6xx_dpm.c:187-188 (programs 0 on the failure branch) and folds into the feedback divider at :184 β€” wrong SPLL/MPLL programming (display/GPU instability) from VBIOS-dependent atom behavior. rv6xx instance of the DF-3976/DF-4119 family (last member).

Chain

radeon_atom_get_clock_dividers fails/succeeds -> inverted branch picks default on success, zeroed ref_div+1=1 on failure -> pi->spll_ref_div/mpll_ref_div wrong -> rv6xx_output_stepping:184-190 programs wrong PLL ref/fb dividers

Kernel refs

Fix

Two-character fix per branch (matches every sibling dispatch).

fix.diff applies cleanly against the read-only sys/ tree (2-file for DF-4125; note the radeon_pm.c hunk also needs dpm_failed_unlock: mutex_unlock(&rdev->pm.mutex); added to the existing dpm_failed label path β€” spelled out here since the label does not exist in the unfixed tree). Kernel-level fix validation is not possible on this guest (no i915/RV6xx devices; the drivers never attach in QEMU).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

kernel-level validation not possible on this guest (no i915/RV6xx hardware); fix is the family-standard guard, diff authored post-verification

fix.diff + VERDICT.md trace
↓ fix.diffDragonFly 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

Confirmed kernel references

Detail

Exploit chain

radeon_atom_get_clock_dividers fails/succeeds -> inverted branch picks default on success, zeroed ref_div+1=1 on failure -> pi->spll_ref_div/mpll_ref_div wrong -> rv6xx_output_stepping:184-190 programs wrong PLL ref/fb dividers

Evidence (decisive lines)

VERDICT.md side-by-side with rv770_dpm.c/r600_dpm.c semantics

PoC changes

no PoC built β€” hardware-gated; evidence is the traced source chain + fix.diff

Verified recommended fix

Two-character fix per branch (matches every sibling dispatch).

Verdict

Source-certain logic inversion (every sibling dispatch uses if (!ret); cypress DF-4119 and btc DF-3976 are the same line-shape). Hardware-gated, no observable impact without RV6xx silicon; the deterministic consequences are: success path ignores ATOM dividers (uses 4), failure path consumes the callee-zeroed struct and programs ref divider 0. Not memory-unsafe β€” radeon_atombios.c:2838 memsets the struct, so unlike the cypress wording there is no uninitialized read here.