Spread-spectrum depth clk_v computed with wrapping u32 numerators β silent wrong SS programming (sclk twin of DF-4858's terminal effect, arithmetic class)
Summary
Both clk_v numerators (4*percentage*fbdiv and 0x40000*percentage*fb) wrap u32 for any real SS percentage (>~0.6%/0.8%) - the wrapped quotient is OR-ed into the 26-bit field with SSEN armed, so SS-enabled boards silently run wrong (replica shows ZERO) depth. Replicated verbatim on guest (exit 98 baseline vs exact depths fixed); u64 fix compile-validated on radeon.ko -Werror. DF-4858's wrong-variable store shape ABSENT here (rv740 programs both words correctly).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4931 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rv740_ssv.c | β | 8.0 KB | view raw | |
| build.sh | β | 109 B | view raw | |
| run.sh | β | 117 B | view raw | |
| build.log | β | 36 B | view raw | |
| run.log | β | 3.5 KB | view raw | |
| radeon_fix_apply.log | β | 719 B | view raw | |
| radeon_module_build.log | β | 2.7 KB | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| env.txt | β | 965 B | view raw | |
| VERDICT.md | β | 4.3 KB | β raw |
DF-4931 VERDICT
Status: reproduced (logic level, verbatim userland replica on the audit guest); kernel trigger hardware-gated (no RV740 GPU in the guest β 0 PCI 0x1002 devices), per the DF-4857/DF-4858 convention for this driver family.
The defect
rv740_populate_sclk_value and rv740_populate_mclk_value compute the
spread-spectrum depth clk_v with 32-bit unsigned numerators whose operands are
VBIOS-derived:
- sclk β
sys/dev/drm/radeon/rv740_dpm.c:166u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);ss.percentageis u16 from the VBIOSASIC_InternalSS_Infotable (radeon_atombios.c:1546area);fbdivis the engine PLL feedback divisor computed at:144-146and is inherently large (~1.8e7 for a 740 MHz engine clock β the accepted DF-4857 pack's "ok-mode" control measured fbdiv=18204444 on the sibling rv730 path). - mclk β
sys/dev/drm/radeon/rv740_dpm.c:259-260u32 clk_v = 0x40000 * ss.percentage * (whole_fb_div + (frac_fb_div / 8)) / (clk_s * 10000);whole_fb_div/frac_fb_divcome from the AtomBIOS memory-PLL computation (:205-208).
C evaluates the full numerator product (left-to-right, both operands u32) before dividing, so any product > 2^32-1 wraps. Thresholds with ordinary values:
- sclk: wraps whenever
percentage * fbdiv > 2^30; with fbdiv β 3.6e6 that is percentage > 59, i.e. any engine SS deeper than ~0.6% (in 0.01% units). - mclk: wraps whenever
percentage * whole_fb_div > 16384; with fb β 200 that is percentage > 82 (~0.8%).
The wrapped quotient then lands in the 26-bit CLK_V field of
CG_SPLL_SPREAD_SPECTRUM_2 (:172-173, mask 0x3ffffff β rv740d.h:108) and the
26-bit CLKV field of MPLL_SS1 (:262-263, rv740d.h:112), while the correct
value also fits the field β so the wrap is completely silent: SSEN gets armed
(:170) with a wrong (often near-zero) depth on every SS-enabled board whose
VBIOS SS percentages cross the (very low) threshold.
This is a sibling of the accepted DF-4858 (rv730 wrong-variable store β different mechanism, same terminal effect: SS enabled with wrong/zero depth) and is arithmetic-adjacent to DF-1992 (div-by-zero in the same blocks β different defect class: this is CWE-190 wrap, not CWE-369; DF-1992 is NOT re-reported here). The rv740 stores themselves are correct (rv740_dpm.c:262-266 programs CLKV into mpll_ss1 and CLKS into mpll_ss2 β the DF-4858 typo shape does NOT exist in rv740).
Reproduction (run.log, audit guest, cc 8.3)
Baseline = kernel arithmetic verbatim:
| case | numerator exact | u32 wrapped | programmed clk_v | correct clk_v |
|---|---|---|---|---|
| S1 sclk 6% SS | 8,621,623,200 | 31,688,608 | 0 (SSEN, zero depth) | 191 |
| S2 sclk 30% SS | 43,108,116,000 | 158,443,040 | 3 | 957 |
| M1 mclk 2% SS | 10,485,760,000 | 1,895,825,408 | 227 | 1258 |
| M2 mclk 5% SS | 26,214,400,000 | 444,596,224 | 53 | 3146 |
All four exact values fit the 26-bit field β silent misprogramming; exit 98.
Fixed build (-DFIX_DF4931): every programmed depth equals the 64-bit exact
value; exit 0.
Impact ceiling
Wrong SMC payload contents for GPU firmware only: spread spectrum armed with a wrong depth (engine and/or memory clock). Fixed-size register words, no OOB, no kernel memory corruption, no pointer involvement, no escalation chain. Realistic worst case is mclk/sclk instability or a GPU wedge (functional DoS of the graphics stack) on SS-enabled RV740-family boards. Same threat position as DF-1992 (VBIOS-supplied values; no unprivileged write path β DPM tables build at init / root-driven power-state transitions).
Fix validation
fix.diff (64-bit numerators, u64 clk_v/(u64) casts, field-masked stores):
applied to the guest's /usr/src (patched rv740_dpm.c md5
3892509a4dee23072ec4bd42e5a68b72, pristine 98e600ceβ¦), radeon.ko rebuilt
under -Werror β compile+link OK (radeon_module_build.log, rv740_dpm.o in the
link line). Harness fixed mode matches exact math. Guest source then reverted
(md5 re-verified pristine). No .orig leftovers.
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
fixedfixed: fix.diff (64-bit numerators + masked stores) applied to guest /usr/src; radeon.ko rebuilt under -Werror with compile+link OK (radeon_fix_apply.log / radeon_module_build.log, rv740_dpm.o in link line); harness baseline exit 98 (4 wrapped depths) vs harness_fixed exit 0 with exact depths; guest source reverted (md5 re-verified pristine 98e600ce6e9b55011b16e2fe5a0302d8).
fix.diff; radeon_fix_apply.log; radeon_module_build.log; run.log (fixed sections); VERDICT.md
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (decisive: baseline 'numerator 4*pct*fbdiv: exact=8621623200 u32-wrapped=31688608 << WRAPS u32', '[BUG] SSEN programmed with wrong depth: 0 instead of 191', exit=98; fixed 'RESULT: all programmed depths match the exact 64-bit math', exit=0); rv740_ssv.c (verbatim replica); radeon_fix_apply.log + radeon_module_build.log (patched build under -Werror); VERDICT.md
PoC changes
Authored fresh (no seed). Two host-compiler absence forced guest builds; one iteration each on the two dce110 harnesses (forward declarations, unbuffered stdout for pre-crash evidence); the rv740 replica built clean on the first guest attempt.
Verified recommended fix
Compute clk_v with 64-bit numerators before the field store: u64 clk_v = 4ULLss.percentagefbdiv/((u64)clk_s10000) at rv740_dpm.c:166 and (u64)0x40000ULLss.percentage(whole_fb_div+frac_fb_div/8)/((u64)clk_s10000) at :259-260, masking the stores with CLK_V_MASK/CLKV_MASK.
Verdict
Reproduced at logic level with a verbatim userland replica of the two rv740_dpm.c spread-spectrum blocks (sclk :159-175 with clk_v at :166, mclk :246-268 with clk_v at :259-260; masks/shifts from rv740d.h:100-116) run on the audit guest (cc 8.3); the kernel-level trigger is hardware-gated (no RV740 GPU β 0 PCI 0x1002 devices), per the DF-4857/DF-4858 convention for this family. Both clk_v numerators (4ss.percentagefbdiv; 0x40000ss.percentage(whole_fb_div+frac/8)) are evaluated in 32-bit unsigned arithmetic over VBIOS-derived operands and wrap for entirely plausible values β sclk wraps for any SS percentage > ~0.6% (fbdiv~3.6e6), mclk for > ~0.8% at fb~200. The wrapped quotient is OR-ed into the 26-bit CLK_V (CG_SPLL_SPREAD_SPECTRUM_2) / CLKV (MPLL_SS1) SMC fields while SSEN is armed β the exact value also fits the field, so the misprogramming is silent: replica shows SSEN with depth 0 (expected 191) and CLKV=227 (expected 1258). Distinct from DF-1992 (CWE-369 zero divisors at :145/:165-166/:258-260 β not re-reported) and from DF-4858 (rv730 wrong-variable store β the rv740 stores at :262-266 are correct). Impact ceiling is wrong GPU firmware values (clock spread instability / GPU wedge): fixed-size register words, no OOB, no kernel memory corruption, no escalation chain. Fix validated: fix.diff (u64 numerators + field-masked stores) applied to guest /usr/src (patched md5 3892509a), radeon.ko rebuilt under -Werror (link OK, radeon_module_build.log), harness_fixed matches exact 64-bit math on all scenarios; guest source reverted (md5 re-verified pristine).
No comments yet.