⬢ DragonFlyBSD Kernel Audit
DF-5619 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5619",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED on the stock guest kernel: a crafted _CST (planted via the proven hint.acpi.0.rsdp + QEMU-loader rig) containing a C2 state whose SystemIO GAS has Address=0 is rejected by acpi_bus_alloc_gas (acpi.c:1280, res stays NULL) yet acpi_cst_md_cx_setup returns SUCCESS for it (acpi_cstate_machdep.c:91-93 Intel branch, identical hole in the non-Intel branch :81-87) even though the bus-space enter method is only installed when res != NULL -- the parser then hits KASSERT(cx_ptr->enter != NULL) (acpi_cpu_cstate.c:612) and boot dies with panic: C2 enter is not set during the unattended cpu_cst0 attach; ssh never answers. A second crafted shape (a _CST whose single C2 FixedHW descriptor fails mwait validation, e.g. BitOffset != 2) exposes the companion hole: acpi_cst_cx_probe_cst still parses as success with the rejected state recorded in cst_non_c3, acpi_cst_postattachs NO_C3-quirk path resurrects cst_cx_count = cst_non_c3+1 (acpi_cpu_cstate.c:746-750), and acpi_cst_startups UNCONDITIONAL panic(\"C%d startup setup failed\") (:842-844) fires -- panic: C1 startup setup failed: 22 -- reproduced identically on a verified non-INVARIANTS kernel (noinv snapshot; KASSERT string absent from the binary), proving the production-kernel boot panic. On the non-INVARIANTS kernel the nullenter shape leaves the enter==NULL state RESIDENT in the live idle table (cx_supported C1/10) with the NULL cx_next->enter call at :970 proven by trace. Ceiling: firmware/AML-driven unattended boot-time kernel panic (CWE-476-adjacent DoS) under the crafted-firmware/loader threat model; family-calibrated Low (same tier as DF-5583). No memory corruption; no user-triggerable path without ACPI-table write access.",
  "exploit_chain": "",
  "evidence": "run.attack_panic.log + panic.txt: panic: C2 enter is not set with backtrace acpi_cst_cx_probe_cst -> acpi_cst_attach during cpu_cst0 attach; run.attack_zerostate_panic.log: bootverbose C2: BitWidth(vendor) 8 ... then panic: C1 startup setup failed: 22 in acpi_cst_postattach; run.noinv_zerostate_panic.log + panic.noinv.txt: the same unconditional panic on the non-INVARIANTS kernel; run.noinv_nullenter_survives.txt: enter-less state resident (C1/10) on the production kernel; run.control_sysctl.txt: same rig, C1-only _CST boots to ssh with C1/1; run.fixedkernel_nullenter.txt + run.fixedkernel_control.txt: with fix.diff, both attack blobs boot to ssh with no usable Cx state in _CST + FADT C1 fallback, control unchanged.",
  "kernel_refs": [
    "sys/platform/pc64/acpica/acpi_cstate_machdep.c:67",
    "sys/platform/pc64/acpica/acpi_cstate_machdep.c:81",
    "sys/platform/pc64/acpica/acpi_cstate_machdep.c:87",
    "sys/platform/pc64/acpica/acpi_cstate_machdep.c:90",
    "sys/platform/pc64/acpica/acpi_cstate_machdep.c:93",
    "sys/dev/acpica/acpi_cpu_cstate.c:564",
    "sys/dev/acpica/acpi_cpu_cstate.c:593",
    "sys/dev/acpica/acpi_cpu_cstate.c:609",
    "sys/dev/acpica/acpi_cpu_cstate.c:616",
    "sys/dev/acpica/acpi_cpu_cstate.c:746",
    "sys/dev/acpica/acpi_cpu_cstate.c:750",
    "sys/dev/acpica/acpi_cpu_cstate.c:842",
    "sys/dev/acpica/acpi_cpu_cstate.c:844",
    "sys/dev/acpica/acpi_cpu_cstate.c:970",
    "sys/dev/acpica/acpi.c:1280"
  ],
  "poc_changes": "No seeded PoC existed (pass-2 finding). Built gen_cst.py (RSDP->RSDT->FADT/DSDT/MADT chain, cloned from the proven DF-5483 generator) + mkdsdt.py (6-Processor DSDT with scenario _CST packages; iasl 20251212 on the guest; first DSL draft used commas between Processor() siblings and was rejected by iasl -- fixed to blank-line separation). Fixed-kernel build applied the combined DF-5619+DF-5620 diff via patch -p1 in the guests /usr/src.",
  "attempts": 7,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline panic kernel; fix-validation kernel: DragonFly 6.5-DEVELOPMENT #1: Thu Oct  8 20:54:15 UTC 2026; non-INVARIANTS production-shape kernel from the noinv-installed snapshot)",
  "runtime_sec": 7500,
  "guest_dirty": 0,
  "build_cmd": "python3 mkdsdt.py <scenario> dsdt_cst_<scenario>.dsl; guest: iasl dsdt_cst_<scenario>.dsl; host: python3 gen_cst.py dsdt_cst_<scenario>.aml /tmp/opencode/cst_<scenario>.bin  |  fix kernel: cd /usr/src && patch -p1 < cst_combined.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC",
  "run_cmd": "vm.sh run_root printf \"hint.acpi.0.rsdp=0x40000000\\nboot_verbose=YES\\n\" >> /boot/loader.conf; vm.sh down; QEMU_EXTRA=\"-machine accel=tcg -cpu SandyBridge -device loader,file=/tmp/opencode/cst_nullenter.bin,addr=0x40000000,force-raw=on\" vm.sh up 240  # TIMEOUT -> vm.sh status == down, boot.log ends with panic: C2 enter is not set (zerostate blob: panic: C1 startup setup failed: 22)",
  "code_hash": "3740da50c9b67bb99c9aee24dce879daa7efcd53df56cf7b6a6a3bdacc779bd2",
  "notes": "Two panic shapes share the root cause unusable/rejected descriptors leak into the live C-state table: (1) accepted-with-enter-NULL (md returns 0 without an entry method), (2) rejected-state resurrection via cst_non_c3 + zero-state acceptance in the parser. The zerostate panic is INVARIANTS-independent (plain panic()). The nullenter shape on a production kernel leaves enter==NULL in the idle table (observed resident on the noinv kernel); the actual NULL call at acpi_cpu_cstate.c:970 is code-trace-proven (idle counters stayed frozen during the observation window, recorded honestly in run.noinv_nullenter_survives.txt). Negative results this file: mwait hint validation itself is sound (cpu_mwait_hint_valid bounds cx_idx/sub against CPUID-derived tables, machdep.c:3384-3397); the C1 FFH early-return is safe (caller pre-installs halt); the BM preamble selection (acpi_cstate_machdep.c:106-123) matches its comments; the monitor/mwait asm (monitor.s) uses the validated hint verbatim.",
  "recommended_fix": "acpi_cst_md_cx_setup: reject non-C1 SystemIO/SystemMemory descriptors whose register resource was not allocated (cx->res == NULL -> EINVAL, both vendor branches); acpi_cst_cx_probe_cst: fail with ENXIO when a _CST yields zero usable states so the driver falls back to FADT C1.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff (md res==NULL rejection + parser zero-state ENXIO) applied in guest /usr/src together with DF-5620s mwait fix, make -j6 nativekernel rc=0 -Werror-clean (322.91s), installkernel, reboot. nullenter blob on patched kernel: NO panic, boots to ssh, prints cpu_cst0: no usable Cx state in _CST + switching to FADT Cx mode, cx_supported C1/0 (FADT C1 halt idle). zerostate blob: same graceful fallback, boots. control blob: unchanged (C1/1, no fallback message). Baseline stock kernel with the same blobs: panics as above.",
  "fix_evidence": "run.fixedkernel_nullenter.txt (uname #1 Oct 8 20:54:15 + no usable Cx state in _CST + switching to FADT Cx mode + cx_supported C1/0, for both nullenter and zerostate blobs) and run.fixedkernel_control.txt (C1/1 unchanged), versus run.attack_panic.log / run.attack_zerostate_panic.log (stock kernel panics); diff in fix.diff; build was -Werror clean (build.log)."
}