# DF-5145 VERDICT — ipmi_acpi SSIF dead-softc attach → NULL `ipmi_enqueue_request` call on detach

## Verdict: REPRODUCED (panic, firmware/config-gated) — fix validated

## The chain (all lines against the pristine audit tree)

1. **Trigger** — firmware (DSDT/SSDT) describes an `IPI0001` ACPI device with
   `_IFT = 4` (`SSIF_MODE`, sys/dev/misc/ipmi/ipmivars.h:132). Reproduced with a
   hand-assembled SSDT (`ssdt_gen.py`; `_HID` EISAID 0x01000926 = byte-swapped
   "IPI0001", verified against QEMU's own isa-ipmi-kcs AML) injected via QEMU
   `-acpitable`.
2. **Probe** — `ipmi_acpi_probe` (ipmi_acpi.c:58-72): `ipmi_attached` guard
   passes (0), `ACPI_ID_PROBE` matches IPI0001 → attach runs. The singleton
   guard itself is present and correct in this frontend (unlike DF-5076's
   ipmi2_pci).
3. **The bug** — `ipmi_acpi_attach` (ipmi_acpi.c:104-109): the `SSIF_MODE`
   case reads `_ADR`, prints "SSIF interface not supported on ACPI", and
   **`return (0)` — attach SUCCESS on a completely uninitialized softc**:
   - `sc->ipmi_enqueue_request` is NULL (only `ipmi_kcs_attach`
     (ipmi_kcs.c:487) / `ipmi_smic_attach` (ipmi_smic.c:393) set it),
   - `lockinit`/`cv_init`/`TAILQ_INIT` never ran (they live in
     `ipmi_startup` (ipmi.c:719-721), reached only via the config_intrhook
     that `ipmi_attach()` (never called) establishes),
   - no cdev, no kthread, and `ipmi_attached` stays 0.
4. **The crash** — any detach of that device. Proven on the `kldunload` path:
   `devclass_delete_driver` → `device_detach` → `ipmi_detach` (ipmi.c:869) →
   `ipmi_set_watchdog(sc, 0)` (ipmi.c:887, DragonFly-specific — FreeBSD's
   ipmi_detach does not do this) → `ipmi_submit_driver_request`
   (ipmi.c:584) → `IPMI_LOCK` on the zeroed lock (survives by accident) →
   **`sc->ipmi_enqueue_request(sc, req)` — indirect call through NULL**.

## Observed (guest, kernel built from the audit tree, QEMU `-acpitable`)

Baseline (pristine module `ipmi-audit.ko` built from /usr/src):

```
ipmi0: <IPMI System Interface> port 0xca2-0xca3 on acpi0
ipmi0: SSIF interface not supported on ACPI
# kldunload ipmi-audit
Fatal user address access from kernel mode from kldunload at 0000000000000000
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x0
instruction pointer   = 0x8:0x0
current process = 808 (kldunload)
Stopped at 0:
db>
```

(panic.txt — full serial capture; guest wedged in DDB, ssh dead.)

Note on the reboot path: a plain `reboot` of the kldloaded configuration shut
down cleanly (the module's SYSUNINIT(ipmi_unload, ipmi.c:940) did not tear the
device down on that path in this test). The deterministic trigger is module
unload; a built-in kernel's shutdown/unload paths remain exposed for the same
reason (any `ipmi_detach` on the dead softc).

## Fix validation (same kernel, same SSDT, fix.diff applied → ipmi-fixed.ko)

```
ipmi0: <IPMI System Interface> port 0xca2-0xca3 on acpi0
ipmi0: SSIF interface not supported on ACPI
device_probe_and_attach: ipmi0 attach returned 6     <- refused, no dead softc
# kldunload ipmi-fixed
UNLOAD_RC_0_CLEAN                                     <- no panic, guest alive
```

fix.diff = (a) ipmi_acpi.c: SSIF case returns ENXIO instead of claiming the
device with a dead softc (also drops the now-unused `info`/`flags` locals);
(b) ipmi.c `ipmi_submit_driver_request`: refuse the NULL enqueue (defense in
depth for any other dead-softc path).

## Why the guest needed an audit-tree kernel

The stock prebuilt guest kernel is built from NEWER source (its ipmi.ko
contains the post-audit `acpi_id_probe_t` framework; the audit tree's
`ACPI_ID_PROBE` comes from the generated acpi_if.h) and its ACPI enumeration
ignores devices added via RSDT SSDTs (verified: neither my IPI0001 nor a
PNP0C0C control device got children, while QEMU's own DSDT-appended IPI0001
KCS device attached fine). A kernel built from the audited /usr/src
(`make nativekernel KERNCONF=X86_64_GENERIC`) enumerates the SSDT device and
exhibits the audited code's behavior.

## PoC engineering notes (what had to be right)

- EISAID for `Name(_HID, ...)` is stored byte-swapped: "IPI0001" →
  0x01000926 (ACPICA byte-swaps before decoding; confirmed against QEMU's
  generated DSDT bytes `0c 26 09 00 01`).
- ACPICA's 2-byte PkgLength form is `(byte1 << 4) | (byte0 & 0x0F)`
  (psargs.c AcpiPsGetNextPackageLength), not the bits[5:0]+<<6 form — a wrong
  length silently truncates the Device body after N names (this cost several
  debug iterations: _HID/_UID/_IFT existed, _SRV/_ADR/_STA/_CRS didn't).
- `Buffer(N){...}` needs the BufferSize TermArg (`0a N`) before the byte list.
- The small I/O-port descriptor's `_LEN` is one byte.
- `_SRV` must exist (ipmi_acpi.c:89 reads it before the interface switch).
- Failed-attach newbus children are not re-probed when another driver
  registers — each module test needs a fresh boot.

## Impact

- Firmware/config-gated local kernel panic (CWE-476): any machine whose ACPI
  tables describe an SSIF BMC via IPI0001 (or any operator of a VM/cloud image
  with injected ACPI tables — trivially done with `-acpitable`) gets a
  deterministic NULL function-pointer call in kernel mode on ipmi module
  unload/teardown. No control over the call target (address 0), no data
  corruption, no info leak: availability only. Severity Low per the project
  rubric (requires specific firmware/configuration); a maintainer-fixable
  robustness defect inherited from FreeBSD's ipmi_acpi.c SSIF stub plus
  DragonFly's detach-time watchdog-off call.

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