β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4461

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4461 Β· 12 files
FileTypeDescriptionSize
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.sh minimal trigger
  • build.sh / run.sh exact commands
  • run.log decisive guest run (untrimmed)
  • env.txt guest environment
  • VERDICT.md full narrative + code trace
  • fix.diff proposed fix (never applied to sys/)
  • manifest.json / verdict.json
VERDICT.md
↓ download raw

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:

  1. Direct sysctl read β€” every probed CCD gets a world-readable CTLTYPE_INT|CTLFLAG_RD sysctl (amdtemp.c:1019-1022, :1077-1080). Any local user can poll dev.amdtemp.N.ccdX in a loop (amdtemp_sysctl amdtemp.c:810 β†’ default branch β†’ amdtemp_gettemp17to19h).
  2. 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)

  1. 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 neither amdsmn nor amdtemp can attach (amdtemp_match amdtemp.c:288-306 requires an AMD vendor/device id on the hostb parent; SMN models also need an amdsmn child, amdtemp.c:536-543). - dev.amdtemp sysctl tree: 0 OIDs; dmesg: 0 amdtemp mentions.
  2. Trigger executed as nobody: sysctl dev.amdtemp.0.ccd0 β†’ unknown oid, rc=1 (run.log). Guest stayed up (uptime).
  3. 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_testable
baseline no→ patch + rebuild →patched clean

fix.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
↓ fix.diffper-fix-DF-4461

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.