# DF-5622 — Reachable `KKASSERT` panic on firmware-supplied `_PSS` control values (AMD-0F FID/VID shim)

**File:** `sys/platform/pc64/x86_64/cpufreq_machdep.c` (pass 2, GLM 5.3)
**Class:** CWE-617 reachable assertion + CWE-20 improper input validation
**Severity:** Low (firmware-preconditioned local panic; no unprivileged trigger path)
**Status:** static trace certain; **not reproducible on the audit guest** (requires AMD
family-0F CPU with APMI FID/VID bits; guest is Intel-vendor under KVM — live-proven
below).

## What the bug is

`amd0f_set_fidvid()` (cpufreq_machdep.c:67) programs K8 FID/VID MSRs from a
`struct amd0f_fidvid`/`struct amd0f_xsit` pair built **entirely from the ACPI `_PSS`
package control value** (`st_cval`) by `acpi_pst_amd0f_set_pstate()`
(sys/platform/pc64/acpica/acpi_pstate_machdep.c:370-388).

The attach-time validator `acpi_pst_amd0f_check_pstates()`
(acpi_pstate_machdep.c:288-368) validates FID/VID ranges **only for the middle
entries** (i = 1..npstates-2, lines 332-347, EINVAL only when
`hw.acpi.cpu.pstate.strigent_check` is on). The **first (max) and last (min)
entries are checked with `kprintf` only — never rejected** (lines 310-331), and
**no entry anywhere is checked for `vid >= rvo`**, even though
`amd0f_set_fidvid()` asserts exactly that:

```c
sys/platform/pc64/x86_64/cpufreq_machdep.c:91-95
        if ((fv->fid & ~0x1) > (cfid & ~0x1) || cvid > fv->vid) {
                KKASSERT(fv->vid >= xsit->rvo);      /* <-- panics */
        } else {
                KKASSERT(cvid >= xsit->rvo);         /* <-- panics */
        }
```

`KKASSERT` is compiled into the **stock GENERIC kernel**: `options INVARIANTS`
is present in `sys/config/X86_64_GENERIC:56` (verified live in the guest at
`/usr/src/sys/config/X86_64_GENERIC`), and `sys/sys/systm.h:98-101` defines
`KKASSERT` as a `panic()` under `INVARIANTS`.

`rvo` comes from `AMD0F_PST_CTL_RVO(st_cval)` (2 bits, 0..3,
acpi_pstate_machdep.c:381) and passes validation for *any* value: the only
check is `rvo_mv % mvs_mv != 0` (acpi_pstate_machdep.c:361) which is always
false when `mvs == 0` (mvs==0 is hard-enforced at acpi_pstate_machdep.c:353).

## Trigger (preconditions)

1. CPU: AMD family 0F (K8) with APMI FID/VID bits — `acpi_pst_amd_probe()`
   returns the `acpi_pst_amd0f` ops table (acpi_pstate_machdep.c:173-175).
2. Firmware supplies a `_PSS` whose entry 0 (or last entry) has
   `ctl.VID < ctl.RVO` (e.g. vid=0, rvo=3) — unchecked per above.
3. Any transition to that state: first P-state transition at attach (via
   `acpi_pst_postattach`), `powerd` at boot (adaptive mode), or root writing
   `hw.acpi.cpu.pstate.domainX.select` / `hw.acpi.cpu.pstate.global`
   (root-only per sys/kern/kern_sysctl.c:1446 `SYSCAP_NOSYSCTL_WR`).

Both assert branches are reachable: the `then` branch fires whenever the
target state's `vid < rvo` on a frequency *increase* (or when current vid is
numerically above target); the `else` branch fires on a *decrease* when the
hardware's current `cvid` (K8s typically boot at max voltage, i.e. low cvid
value) is below `rvo`.

Result: `panic("assertion \"fv->vid >= xsit->rvo\" failed in amd0f_set_fidvid ...")`
on the stock kernel, unattended at boot with default `powerd`.

## Why the audit guest cannot exercise it (live-proven)

The guest is `QEMU Virtual CPU version 2.5+`, vendor `GenuineIntel`
(family 0xf/model 0x6b), KVM. `acpi_pst_md_probe()` gates on vendor first
(acpi_pstate_machdep.c:150-153 → `acpi_pst_intel_probe()`, no EST bit → NULL).
The KLD harness `pstprobe.c` in this pack calls the real kernel function
`acpi_pst_md_probe()` (global symbol) and dumps the gates:

```
pstprobe: cpu_vendor_id=0x8086 (AMD=0x1022 INTEL=0x8086) family=0xf model=0x6b
pstprobe: APMI(0x80000007) edx=0 (amd0f needs (edx & 0x06) == 0x06)
pstprobe: acpi_pst_md_probe() = NULL: no P-state machdep; acpi_pst_amd0f ops NEVER selected -> amd0f_set_fidvid UNREACHABLE on this CPU
pstprobe: symbols in running kernel: amd0f_set_fidvid=0xffffffff80bda6c0 amd0f_get_fidvid=0xffffffff80bdaac0 amd0f_fidvid_limit=0xffffffff80bdab00
```

i.e. the amd0f helpers **are compiled into the running GENERIC kernel**
(alive code — this file is NOT part of the dead `#ifdef notyet` legacy CPU
attachment, sys/bus/pci/x86_64/legacy.c:233-347), but their execution requires
AMD-0F hardware that this guest cannot present (QEMU/KVM does not implement
the K8 FID/VID MSR pair 0xc0010041/0xc0010042; the harness deliberately never
calls the amd0f_* functions — their first instruction is `rdmsr(0xc0010042)`
which raises #GP on this CPU; that #GP class is already filed as DF-5484 for
the sibling file).

The trigger itself, on suitable hardware/a suitably configured VMM, is a
crafted SSDT `_PSS` (e.g. `Name (_PSS, Package (0x02) { Package (0x06) { 2000, 89000, 10, 10, 0x40c00a00, 0x40c00a00 }, ... })`
— ctl encoding `IRD=1|IRT=0|RVO=3|PLLTIME=1|MVS=0|VST=10|VID=0|FID=40`)
passed with `-acpitable`/`-bios` or flashed firmware.

## Reproduce the introspection evidence (this guest)

```
./build.sh   # builds pstprobe.ko against /usr/src + /usr/obj kernel (bsd.kmod.mk)
./run.sh     # kldload + dmesg   (see run.log / build.log for captured output)
```

## Fix

Return an error instead of asserting on firmware data (see `fix.diff`):
validate `vid >= rvo` (and the `cvid >= rvo` downgrade case) with `return
EINVAL`, and harden the attach-time validator in
`sys/platform/pc64/acpica/acpi_pstate_machdep.c` to reject endpoint entries
whose FID/VID are out of the MSR-derived `[fv_min, fv_max]` envelope instead
of `kprintf`-only, so a malicious/buggy `_PSS` is rejected at attach rather
than panicking (or silently programming out-of-spec FID/VID on non-INVARIANTS
kernels) at first transition.
