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)
PoC verification
Evidence pack
findings/poc/DF-4126 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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(..., ÷rs);
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
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(..., ÷rs);
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
- sys/dev/drm/radeon/rv6xx_dpm.c:1957
- sys/dev/drm/radeon/rv6xx_dpm.c:1964
- sys/dev/drm/radeon/rv6xx_dpm.c:184
- sys/dev/drm/radeon/rv6xx_dpm.c:187
- sys/dev/drm/radeon/radeon_atombios.c:2838
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_testablekernel-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
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.
No comments yet.