amdtemp_gettemp17to19h KASSERTs on mutable CCD-valid hardware state β unprivileged sysctl read or the periodic sensor task panics stock INVARIANTS kernels
Summary
Every CCD temp read asserts the VALID bit instead of handling invalid - but the bit is live SMU state and the driver's own probe paths skip clear-bit sensors, proving it can be 0. Two unpriv-reachable entries: world-readable dev.amdtemp.N.ccdX sysctls and the 2-second sensor_task (no user action needed). Stock X86_64_GENERIC enables INVARIANTS. Local DoS on Zen via 'while true; sysctl dev.amdtemp.0.ccd*'. Hardware-gated on guest (no AMD PCI) - honestly not_reproduced. Handle-invalid-like-probe fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4461 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 4.1 KB | β raw | |
| amdtemp_ccd_read.sh | β | 1.5 KB | view raw | |
| build.sh | β | 79 B | view raw | |
| run.sh | β | 137 B | view raw | |
| build.log | β | 43 B | view raw | |
| run.log | β | 688 B | view raw | |
| run.2.log | β | 688 B | view raw | |
| env.txt | β | 246 B | view raw | |
| fix.diff | β | 845 B | view raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 3.5 KB | view raw |
DF-4461 β PoC evidence pack
What
Unprivileged-triggerable kernel panic in amdtemp(4) on AMD family 17h/19h
(Zen) CPUs: the CCD temperature read path asserts on live hardware state
(AMDTEMP_17H_CCD_TMP_VALID) instead of handling it, and the default kernel
config builds INVARIANTS in.
- File:
sys/dev/powermng/amdtemp/amdtemp.c - Lines: 946-971 (KASSERT at 966-967); probe-time contrast at 1012/1070; sysctl creation at 1019-1022/1077-1080; sensor task at 759-763.
Build
./build.sh # script-only PoC β nothing to compile
Run (on an AMD Zen host, as any unprivileged user)
./run.sh # polls dev.amdtemp.0.ccd0..ccd11 + core0.sensor0
Expected on affected hardware + INVARIANTS kernel: panic
sensor %d: not valid (serial console / /var/crash).
Run on the audit guest (what actually happened)
dfbsd-qemu/vm.sh run_root 'su -m nobody -c sysctl dev.amdtemp.0.ccd0' -> sysctl: unknown oid 'dev.amdtemp.0.ccd0' (rc=1)
The guest is QEMU with no AMD PCI devices; amdtemp never attaches, so the
vulnerable path is absent β see run.log, env.txt, VERDICT.md.
Contents
amdtemp_ccd_read.shminimal triggerbuild.sh/run.shexact commandsrun.logdecisive guest run (untrimmed)env.txtguest environmentVERDICT.mdfull narrative + code tracefix.diffproposed fix (never applied to sys/)manifest.json/verdict.json
DF-4461 β amdtemp read-time KASSERT on mutable hardware state (CCD validity)
Verdict: not_reproduced on this guest β hardware-gated; code-path analysis holds.
Claimed impact: local DoS (kernel panic) on AMD family 17h/19h CPUs running a stock INVARIANTS kernel. Not demonstrable on the audit guest (QEMU, no AMD PCI devices), which is an environment limitation, not a refutation.
Root cause (line-accurate)
sys/dev/powermng/amdtemp/amdtemp.c:
- Probe time β the driver itself treats the CCD valid bit as mutable and possibly clear, skipping such sensors gracefully:
amdtemp_probe_ccd_sensors17h()amdtemp.c:1012 βif ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0) continue;amdtemp_probe_ccd_sensors19h()amdtemp.c:1070 β same check.- Read time β the same bit is enforced with a KASSERT instead of a runtime check:
amdtemp_gettemp17to19h()amdtemp.c:966-967:KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0, ("sensor %d: not valid", (int)sensor));- KASSERT panics whenever the kernel is built with
options INVARIANTS(sys/sys/systm.h:94-96). The default DragonFly kernel config enables it:sys/config/X86_64_GENERIC:56(options INVARIANTS), and the guest kernel confirms it runs X86_64_GENERIC with that config (run.log).
Reachability / threat
Two independent entry points, both unprivileged-reachable:
- Direct sysctl read β every probed CCD gets a world-readable
CTLTYPE_INT|CTLFLAG_RDsysctl (amdtemp.c:1019-1022, :1077-1080). Any local user can polldev.amdtemp.N.ccdXin a loop (amdtemp_sysctlamdtemp.c:810 β default branch βamdtemp_gettemp17to19h). - Periodic sensor task β
sensor_task_register2(..., amdtemp_sensor_task, 2, ...)(amdtemp.c:759-763) refreshes all CCD sensors every 2 s with no user involvement.
If any probed CCD's Tccd register ever reports VALID=0 after boot (CCD power management / SMU state the kernel does not control), the kernel panics. There is no plausible reading under which the bit is guaranteed immutable: the probe-time filter exists precisely because it reads clear on some CCD slots, and nothing pins it after boot.
Secondary dead assertions in the same helpers (context, not the finding):
KASSERT(error == 0, ("amdsmn_read")) at amdtemp.c:941/956/965 can never fire β
amdsmn_read() (sys/dev/powermng/amdsmn/amdsmn.c:280-293) always returns 0.
Verification performed (this run)
- Guest state (run.log, env.txt):
-
DragonFly dfbsd 6.5-DEVELOPMENT #0 ... X86_64_GENERIC-hw.model: QEMU Virtual CPU version 2.5+,hw.ncpu: 6-pciconf -l | grep -c 1022β0β no AMD PCI devices, so neitheramdsmnnoramdtempcan attach (amdtemp_matchamdtemp.c:288-306 requires an AMD vendor/device id on the hostb parent; SMN models also need anamdsmnchild, amdtemp.c:536-543). -dev.amdtempsysctl tree: 0 OIDs; dmesg: 0 amdtemp mentions. - Trigger executed as
nobody:sysctl dev.amdtemp.0.ccd0βunknown oid, rc=1 (run.log). Guest stayed up (uptime). - Conclusion per the audit status table: guest reachable, target device/feature absent β not_reproduced / impact none on this guest. The panic claim stands on the source analysis above; it requires AMD Zen hardware to manifest.
Fix
fix.diff β replace the read-time KASSERT with a graceful invalid reading
(return 0 β 0.0 K, an obviously-invalid value; alternatively the caller
amdtemp_sensor_task could mark the ksensor SENSOR_FINVALID). The probe-time
skip (amdtemp.c:1012/1070) already defines the intended semantics: a clear
VALID bit is a normal condition, not an invariant violation.
Fix validation: not possible on this guest (requires AMD SMN hardware);
fix_status: not_testable.
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.diff replaces the KASSERT with an early return of 0 for an invalid CCD reading (matching the probe-time semantics at amdtemp.c:1012/1070). Cannot be built/tested on the audit guest: the vulnerable path requires AMD SMN hardware which the QEMU guest does not emulate, so baseline never reproduced and the patched kernel would be equally untestable.
fix.diff in this directory; guest hardware absence documented in run.log/env.txt
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (unknown-oid as nobody + uptime proving guest alive; guest config greps), VERDICT.md (full path:line trace incl. probe-vs-read asymmetry), env.txt (uname/QEMU CPU/no AMD PCI), fix.diff (graceful handling replacing the KASSERT)
PoC changes
Seed sketch reduced to its essence: the trigger is a pure unprivileged sysctl read loop (amdtemp_ccd_read.sh); no compilation needed. Guest-side execution via su -m nobody per the audit guest layout.
Verified recommended fix
Replace the read-time KASSERT on AMDTEMP_17H_CCD_TMP_VALID in amdtemp_gettemp17to19h with a graceful invalid-handling return, mirroring the probe-time skip in amdtemp_probe_ccd_sensors17h/19h.
Verdict
Not reproduced on the audit guest: it is QEMU with zero AMD (0x1022) PCI devices, so amdtemp cannot attach (amdtemp_match requires an AMD hostb id, amdtemp.c:288-306; SMN models additionally need an amdsmn child, amdtemp.c:536-543) and the dev.amdtemp.N.ccdX OIDs do not exist (run.log: 'unknown oid', guest stayed up). The code-path analysis is line-accurate and stands: amdtemp_gettemp17to19h KASSERTs on AMDTEMP_17H_CCD_TMP_VALID at amdtemp.c:966-967 for every CCD sysctl read (world-readable, amdtemp.c:1019-1022/1077-1080) and for the 2s periodic sensor task (amdtemp.c:759-763), while the driver's own probe paths treat the identical bit as possibly-clear (amdtemp.c:1012/1070). KASSERT panics under INVARIANTS (sys/sys/systm.h:94-96) which the default X86_64_GENERIC enables (sys/config/X86_64_GENERIC:56; guest kernel confirmed running it). On AMD Zen hardware this is an unprivileged local panic; claimed impact panic, observed impact none on this hardware-less guest.
No comments yet.