Unsigned underflow in Phase-1 voltage ramp writes malformed MSR 0xc0010041 (reserved bits 13-31 garbage, wrong VID)
Summary
uint32_t val holds cvid-(1<<mvs) int arithmetic - when negative it wraps and the (val>0) clamp is dead code for unsigned types; WRITE_FIDVID shifts the garbage into the MSR (reserved bits set, VID=0x1d instead of 0) - #GP panic or misprogrammed voltage control. Harness-verified arithmetic. Signed-val one-line fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4904 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 1.9 KB | β raw | |
| sim_vidunderflow.c | β | 1.9 KB | view raw | |
| build.sh | β | 106 B | view raw | |
| run.sh | β | 99 B | view raw | |
| build.log | β | 52 B | view raw | |
| run.log | β | 340 B | view raw | |
| env.txt | β | 314 B | view raw | |
| fix.diff | β | 660 B | view raw | |
| manifest.json | β | 914 B | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-4904 β Unsigned underflow in Phase-1 voltage ramp writes malformed MSR 0xc0010041
File: sys/dev/powermng/powernow/powernow.c:231,260-262
Severity: Low Β· Bucket: base:dev Β· Confidence: likely (type bug certain; hardware impact conditional)
Root cause
uint32_t val; /* powernow.c:231 */
while (cvid > vid) { /* powernow.c:260 */
val = cvid - (1 << cstate->mvs); /* powernow.c:261 */
WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); /* powernow.c:262 */
val is uint32_t; cvid - (1 << mvs) is computed in int and goes
negative whenever the current VID is smaller than one MVS step (e.g.
cvid=1, mvs=2 β β3). Assignment wraps to 0xFFFFFFFD, so the intended
clamp (val > 0) ? val : 0 can never fire (unsigned values are always
β₯ 0, and 0xFFFFFFFD > 0 is true). WRITE_FIDVID (powernow.c:76-78)
then shifts that word << 8 into MSR MSR_AMDK7_FIDVID_CTL
(0xc0010041): bits 13-31 (reserved + control fields) receive garbage and
the programmed VID is 0x1d instead of 0.
On K8 silicon, writing reserved bits of FID_VID_CTL either #GPs
(kernel panic) or misprograms the voltage-change control logic. The
path runs whenever a downward voltage transition begins with
cvid < (1 << mvs) β reachable at module load via the DF-4903 route
(powernow_init:392 β setperf drives vid toward 0) or whenever root
writes a lower target to machdep.powernow.frequency.target
(powernow.c:323-335; sysctl writes are privilege-gated,
kern_sysctl.c:1446).
Harness (logic-level, run on guest as uid=1001 β run.log)
cvid=1 vid=0 mvs=2 -> cvid-(1<<mvs) = -3 stored in uint32_t val = 0xfffffffd (val > 0) as evaluated = TRUE <-- underflow slips through WRMSR(0xc0010041) value = 0x000000fffffffd0a VID field (bits 8-12) = 0x1d bits 13-31 (reserved) = 0x7ffff <-- garbage written
Verification status
untested at kernel level: the MSR write requires AMD K8 (guest is
Intel/QEMU; module gate refuses, MOD_LOAD error 2 β env.txt).
Recommended fix
Do the arithmetic in signed and clamp before the unsigned narrowing β
val = cvid - (1 << mvs); WRITE_FIDVID(cfid, (val >= 0 && cvid > (1 << mvs)) ? ... β
see fix.diff (makes val int).
Build / run
cc -Wall -O2 -o sim_vidunderflow sim_vidunderflow.c && ./sim_vidunderflow
DF-4904 β VERDICT
status: untested Β· reproduced: 0 Β· impact: dos (malformed MSR write / #GP panic on K8) Β· confidence: likely
Narrative
k8_powernow_setperf() Phase 1 lowers the core voltage in steps of
1 << cstate->mvs (powernow.c:260-266). val is declared uint32_t
(powernow.c:231) but cvid - (1 << cstate->mvs) is int arithmetic:
when the current VID is below one step (cvid=1, mvs=2 β β3), the value
wraps to 0xFFFFFFFD on assignment. The clamp (val > 0) ? val : 0
(powernow.c:262) is dead code for unsigned types β 0xFFFFFFFD > 0 β so
WRITE_FIDVID (powernow.c:76-78) composes
(1ULL<<32)|(1<<16)|(0xFFFFFFFD<<8)|cfid = 0x000000fffffffd0a and
writes it to MSR 0xc0010041: VID field gets 0x1d instead of 0, and bits
13-31 (reserved/control) get pure garbage.
Effect on K8 silicon: #GP on wrmsr (kernel panic at CPL 0) or
misprogrammed FID/VID transition control. Reachable (a) at module load
through the DF-4903 route (powernow_init:392 drives vidβ0), or (b) when
root writes a lower frequency to machdep.powernow.frequency.target
(writes are privilege-gated, kern_sysctl.c:1446).
Verification
Kernel-level: impossible on this guest β QEMU/Intel CPU, module gate at
powernow.c:358-363 refuses (MOD_LOAD error 2, env.txt). Logic-level:
sim_vidunderflow.c run as uid=1001 reproduces the exact arithmetic and
prints the malformed MSR word (run.log). Not counted as reproduction.
Fix
int32_t val makes the existing clamp effective (fix.diff, applies
clean). Fix build/validation not run (needs K8).
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_testableFix validation not run: path needs AMD K8 hardware. fix.diff (make val int32_t so the existing clamp works) applies cleanly with patch(1).
fix.diff: checking file powernow.c / DF4904_APPLIES_CLEAN
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (uid=1001 harness output showing underflow 0xfffffffd slipping through (val>0) and the malformed MSR word), sim_vidunderflow.c, VERDICT.md
PoC changes
harness authored fresh; WRITE_FIDVID macro replicated exactly from powernow.c:76-78
Verified recommended fix
Declare val as int32_t (signed) so the existing (val > 0) ? val : 0 clamp catches the underflow.
Verdict
Type-level bug is certain: val is uint32_t (powernow.c:231), so cvid - (1<<mvs) underflows to ~4e9 whenever cvid < (1<
No comments yet.