corepower_probe family-0x06 model whitelist is a skip-guard, not a reject: non-family-06 Intel CPUIDs pass probing (attach then runs a raw rdmsr) β live-reproduced hardening gap
Summary
The whitelist is wrapped in if(family==0x06) with no else-reject: a family-0x0f CPUID passes probe on MSR availability alone and attach runs a RAW rdmsr. LIVE-REPRODUCED on the guest (Family=0xf): 'corepower0 ... on cpu5' + 'attach returned 6', no panic, clean unload (the VMM services the MSR). The exact gating shape that produced the reproduced DF-4735 coretemp #GP. Explicit-reject + rdmsr_safe fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4928 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.sh | β | 991 B | view raw | |
| build.sh | β | 121 B | view raw | |
| run.log | β | 877 B | view raw | |
| env.txt | β | 511 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| README.md | β | 2.7 KB | β raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-4928 β corepower probe family-gating fall-through (Info, hardening)
File: sys/dev/powermng/corepower/corepower.c
Class: incorrect CPU gating β probe passes on non-family-06 CPUs; raw
rdmsr() on the attach path where rdmsr_safe() was available.
Severity: Info (no demonstrated security impact β every subsequent MSR
read remains gated by corepower_try()/rdmsr_safe; live-verified benign).
Verified live: yes β on the QEMU/KVM audit guest whose CPU reports
Origin="GenuineIntel" Family=0xf Model=0x6b.
The issue
corepower_probe() (corepower.c:154-205) wraps the model whitelist in
if (cpu_family == 0x06) {
switch (cpu_model) { ... default: return (ENXIO); }
}
For a CPU whose family is not 0x06 (this guest: family 0xf) the switch
is skipped β probe falls through to the only remaining gate,
corepower_try(MSR_RAPL_POWER_UNIT) (rdmsr_safe). If that MSR read happens
to succeed (real family-0xf Netburst hardware does not implement RAPL, but
hypervisors commonly service unknown MSRs β QEMU/KVM does here), probe
succeeds and corepower_attach() runs on a CPU that cannot have the
feature, executing the raw rdmsr() at corepower.c:273 before failing
with ENXIO at :331-332 (sc_have_sens == 0).
Observed live (run.log):
CPU: QEMU Virtual CPU version 2.5+ ; Origin="GenuineIntel" Id=0x60fb1 Family=0xf Model=0x6b corepower0: <CPU On-Die Power Usage Estimation> on cpu5 device_probe_and_attach: corepower0 attach returned 6 <- ENXIO from attach (no sensors - as expected)
The same skip-guard shape exists in corepower_attach() (:226-271) β there
it is harmless because sc_have_sens stays 0 β ENXIO.
Why this matters even though it is benign today: this is the exact pattern
that produced the reproduced #GP family in coretemp (DF-4735) β probe
gating that trusts one MSR check and attach/refresh paths that then use
raw rdmsr(). In corepower every post-probe read is either
rdmsr_safe()-verified (each sensor MSR at :289-329) or, for
MSR_RAPL_POWER_UNIT at :273, verified by probe's corepower_try() β
so no #GP is reachable; the finding records (a) the inverted-looking
family guard and (b) the defense-in-depth miss, with a two-line fix.
Reproduce (on the audit guest, as root)
sh trigger.sh # kldload corepower; capture dmesg; kldunload; uptime
Success criterion: dmesg shows the probe/attach pair above on a family-0xf CPU, no sensors attach, no panic, guest alive after unload.
Files
trigger.sh/build.sh(no-op) /run.log/env.txtfix.diffβ reject non-family-06 in probe;rdmsr_safeat attach:273VERDICT.md,manifest.json,verdict.json
DF-4928 VERDICT β corepower family-gating fall-through (Info)
Bottom line
Reproduced live on the audit guest; impact: none (by design of the
finding). On the QEMU/KVM guest (CPUID Origin="GenuineIntel"
Family=0xf Model=0x6b), corepower_probe() accepts the device because
the family-0x06 model whitelist is a skip-guard, not a reject
(corepower.c:165-197), leaving rdmsr_safe(MSR_RAPL_POWER_UNIT) (via
corepower_try, :199) as the only gate β which QEMU passes since it
services the MSR. corepower_attach() then executes the raw
rdmsr(MSR_RAPL_POWER_UNIT) at corepower.c:273 before returning ENXIO at
:331-332 (sc_have_sens == 0). No sensors attach, no #GP, kldload and
kldunload are clean, guest stays up (run.log).
Why it is only Info
- Every sensor MSR is individually verified with
rdmsr_safe()at attach (corepower_tryat :289-:329) before the refresh path's rawrdmsr()(:393, :402) can ever touch it. MSR_RAPL_POWER_UNITat :273 is verified by probe'scorepower_try()on the same package β an MSR readable viardmsr_safecannot #GP when read raw one call later.- The failure mode on any real non-RAPL Intel CPU is a clean ENXIO.
The risk recorded here is the pattern (this is the shape that produced the reproduced coretemp #GP, DF-4735): a gating typo plus raw MSR reads one refactor away from a panic. The fix makes the intent explicit.
Everything else checked in this pass-2 audit of corepower.c (all clean)
- identify/per-package dedup (:116-152):
node->child_nois the count of children at a topology level (subr_cpu_topology.c:122-133), sochild_no == 0is a degenerate-node guard, not an inverted chip filter β initial suspicion of an inverted gating killed. - attach/refresh/detach lifetime:
sensor_task_register2runs callbacks holding the per-CPUthr->lock(kern_sensors.c:300-339) andsensor_task_unregister2takes the same lock before clearingrunningβ aftercorepower_detachreturns, no callback can be running; the deinstall-then-unregister order at :346-347 is safe. Live kldunload clean. - Energy math (:352-410): no division by zero (
secsis the literal 1), the 32-bit wraparound delta is bounded β€ 0xFFFFFFFF (theenergy > abranch impliesres = 2^32 - energy + a β€ 2^32 - 1), so the u64βu32 parameter narrowing at :409 cannot truncate. Units fields are 4-5 bits (__BITS64),1 << 31for a hypothetical energy_units=31 fits uint32_t (pedantic int-shift nit, not reachable on real silicon, not filed). - sysctl strings: all
ksnprintfwithsizeofbounds (:287, :389). - Attach failure paths: ENXIO before any registration, or with no
installed sensordev (sysctl install defers while
sensdev->oid == NULL, kern_sensors.c sensor_sysctl_install);sensor_task_register2cannot return NULL (M_WAITOK), sosensor_task_unregister2(NULL)is unreachable.
Fix validation
fix.diff (reject cpu_vendor_id != CPU_VENDOR_INTEL || family != 0x06
in probe; rdmsr_safe at :273) β two-line hardening, dry-run-applyable;
no kernel rebuild was performed for this Info finding
(fix_status: not_testable, per contract only memory-corruption findings
mandate a single-fix kernel build).
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_testableTwo-line hardening (fix.diff, applies clean via patch --dry-run). No kernel rebuild/validation performed: Info-severity finding, and per contract a single-fix kernel build is mandated only for reproduced memory-corruption findings. The patched behavior is trivially predictable (probe returns ENXIO for family != 6 before any attach attempt).
fix.diff hunks at corepower.c:159 and :275; baseline behavior in run.log.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: CPU line 'Origin="GenuineIntel" Id=0x60fb1 Family=0xf Model=0x6b' followed by kldload -> 'corepower0: <CPU On-Die Power Usage Estimation> on cpu5' + 'device_probe_and_attach: corepower0 attach returned 6', '(no sensors - as expected)', clean kldunload, uptime OK. env.txt: guest uname + hw.model + dev_idmax.
PoC changes
Authored from scratch (Info finding; no prior sketch). Trigger is a pure shell sequence: kldload corepower as root, capture dmesg/sysctl, kldunload, uptime.
Verified recommended fix
Reject non-Intel/non-family-06 CPUIDs explicitly at the top of corepower_probe() and use rdmsr_safe() for MSR_RAPL_POWER_UNIT in corepower_attach() (fix.diff).
Verdict
Reproduced live on the DragonFly 6.5-DEVELOPMENT audit guest (QEMU/KVM, CPUID Origin=GenuineIntel Family=0xf Model=0x6b): corepower_probe() accepts a non-family-06 CPU because the model whitelist at corepower.c:165-197 is a skip-guard (the switch is simply not entered for family != 6) rather than a reject, leaving rdmsr_safe(MSR_RAPL_POWER_UNIT) via corepower_try() (:199) as the only gate - which this VMM passes since it services the MSR. attach() then executes the raw rdmsr() at :273 and returns ENXIO at :331-332 (sc_have_sens==0). dmesg pair 'corepower0:
No comments yet.