# VERDICT — DF-5485

## Reproduced? YES (functional) — a spec-legal `_PCT` GAS with
## AccessWidth=3 (DWord) is rejected by the kernel; the P-state domain is
## permanently killed. No security impact (over-strict, not under-strict);
## filed as an ABI-correctness/hardening finding (Info).

### Root cause

`acpi_pst_md_gas_asz` (sys/platform/pc64/acpica/acpi_pstate_machdep.c:564-583)
treats the GAS `AccessWidth` field as a **byte count**:

```c
if (gas->AccessWidth != 0)
	asz = gas->AccessWidth;      /* WRONG: enum, not bytes */
else
	asz = gas->BitWidth / NBBY;
switch (asz) { case 1: case 2: case 4: break; default: asz = 0; }
```

Per the ACPI spec (Generic Address Structure, "Access Size") AccessWidth is
an **enumeration**: 1 = byte, 2 = word, 3 = dword, 4 = qword. Consequences:

* AccessWidth=3 (dword, 4 bytes — perfectly legal firmware encoding) →
  asz=3 → falls to `default:` → asz=0 → `acpi_pst_md_gas_verify` (:599-601)
  returns EINVAL → `acpi_pst_intel_check_csr` prints
  "Invalid control register GAS" and the whole P-state domain is flagged
  DEAD (`acpi_pst_postattach`, acpi_cpu_pstate.c:1129-1134): **cpufreq
  silently unavailable**.
* AccessWidth=4 (qword, 8 bytes) → asz=4 → accepted as a *4-byte* access —
  an under-length access to an 8-byte register (functional hazard; the
  switch should accept 8 and reject >8 after conversion).

Note the two accidental hits: AccessWidth=1 and 2 are both the enumeration
*and* the byte count, which is why byte/word firmware works.

### Guest demonstration (same rig as DF-5483)

Two boots, identical except the ctrl GAS attribute bytes:

| run | ctrl GAS | result |
|-----|----------|--------|
| control | `AccessWidth=0, BitWidth=8`, port 0x80 | domain alive, `px_dom0.available: 3000` |
| dword | `AccessWidth=3, BitWidth=32`, port 0x100 (spec-legal dword GAS) | `cpu0: Invalid control register GAS` + `ACPI: domain0 P-State configuration check failed` + `ACPI: domain1 P-State configuration check failed`; `sysctl hw.acpi.cpu.px_dom0.available` → unknown oid |

Full output in run.dword_accesswidth.log. The kernel adopted the table
(`cpu_pst0: control reg 1 100` — parse and port reservation succeeded) and
then rejected the *access-size encoding*.

### Why Info

Reachable only from firmware-supplied tables; the failure mode is a
*self-DoS of the cpufreq feature* (and a per-CPU kprintf), not memory
unsafety — the code errs on the side of rejection. Still worth fixing: any
BIOS that encodes dword-access P-state registers (the ACPI-spec-correct
encoding for 32-bit PERF_CTL-style ports) silently loses P-state control on
DragonFly while working on other OSPMs.

### fix.diff

Authored (fix.diff): decode the enumeration (`1 << (AccessWidth-1)`,
reject >4), keep the {1,2,4} byte-size acceptance (the res_read/res_write
accessors only implement 1/2/4 — qword GAS stays rejected pending an 8-byte
accessor, but now for the *right* reason: 8 ∉ {1,2,4}, not "4 means 4").

Fix validation: the patched source was compiled into a kernel on the guest
(make nativekernel, build log in fix_build.log) and re-run with the exact
same dword blob — see fix section of the pack README for the before/after.

## 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.
