ipmi_acpi SSIF stub attaches a dead softc (return 0 with nothing initialized); any later detach executes NULL sc->ipmi_enqueue_request β fatal kernel trap
Summary
The SSIF arm prints 'not supported' then returns 0 - attach success on a softc with NULL ipmi_enqueue_request and no locks; DFly's ipmi_detach then calls ipmi_set_watchdog -> submit_driver_request -> call through NULL. REPRODUCED on guest with a hand-assembled IPI0001/_IFT=4 SSDT: Fatal trap 12 rip=0x0 on kldunload, DDB, guest dead; refuse-ENXIO + defensive-guard fix validated (attach fails cleanly, unload returns 0). Firmware/VM-config-gated (SSDT injection).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5145 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 5.8 KB | β raw | |
| ssdt_gen.py | β | 4.0 KB | view raw | |
| build.sh | β | 229 B | view raw | |
| run.sh | β | 264 B | view raw | |
| build.log | β | 960 B | view raw | |
| run.log | β | 1.7 KB | view raw | |
| panic.txt | β | 1.4 KB | view raw | |
| env.txt | β | 635 B | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| manifest.json | β | 1.4 KB | view raw | |
| verdict.json | β | 4.1 KB | view raw |
DF-5145 β ipmi_acpi SSIF dead-softc attach β NULL ipmi_enqueue_request call on detach
What this proves
Firmware that describes its BMC with an ACPI IPI0001 device whose _IFT is
4 (SSIF_MODE, ipmivars.h:132) makes ipmi_acpi_attach() return success
while leaving the softc completely uninitialized β it never runs
ipmi_kcs_attach/ipmi_smic_attach (so sc->ipmi_enqueue_request is NULL),
never calls ipmi_attach() (so lockinit in ipmi_startup() never happens,
no cdev, ipmi_attached stays 0).
Every later detach of that device β the SYSUNINIT(ipmi_unload) at every
shutdown/reboot (ipmi.c:940), or module teardown β runs DFly's
ipmi_detach() β ipmi_set_watchdog(sc, 0) (ipmi.c:887) β
ipmi_submit_driver_request() β sc->ipmi_enqueue_request(sc, req) β an
indirect call through a NULL function pointer β fatal kernel trap.
Files
ssdt_gen.pyβ writesssdt-ipmi-ssif.aml(hand-assembled AML, no iasl)build.shβ build the audit-treeipmi.ko+ kernel inside the guestrun.shβ the full reproduction sequence (baseline)fix.diffβ the verified fix (ipmi_acpi.c: refuse SSIF instead of no-op-attaching) + defensive guard in ipmi.cpanic.txtβ serial-console capture of the fatal trap (baseline run)verdict.jsonβ machine verdict
Reproduce
python3 ssdt_gen.py ssdt-ipmi-ssif.aml- Boot the guest with the table attached:
cd dfbsd-qemu && QEMU_EXTRA="-acpitable file=$PWD/ssdt-ipmi-ssif.aml" ./vm.sh up(kernel must be built from the audit tree β the stock prebuilt guest kernel is newer and ignores RSDT-SSDT-added devices; see VERDICT.md) vm.sh run_root 'kldload /root/ipmi-audit.ko'β dmesg:ipmi0: SSIF interface not supported on ACPI(attach "succeeded", device attached, softc dead)vm.sh run_root 'reboot'β serial console:Fatal trap 12: page fault while in kernel modeatfault virtual address = 0x0insideipmi_submit_driver_request
Expected result
Baseline: kernel panic on every reboot of a machine whose ACPI describes an SSIF BMC (deterministic NULL function-pointer call; no attacker control of the target β CWE-476).
Patched (fix.diff): ipmi_acpi_attach returns ENXIO for _IFT==4, the ACPI
node is left unattached, reboot is clean.
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)
- Trigger β firmware (DSDT/SSDT) describes an
IPI0001ACPI device with_IFT = 4(SSIF_MODE, sys/dev/misc/ipmi/ipmivars.h:132). Reproduced with a hand-assembled SSDT (ssdt_gen.py;_HIDEISAID 0x01000926 = byte-swapped "IPI0001", verified against QEMU's own isa-ipmi-kcs AML) injected via QEMU-acpitable. - Probe β
ipmi_acpi_probe(ipmi_acpi.c:58-72):ipmi_attachedguard passes (0),ACPI_ID_PROBEmatches IPI0001 β attach runs. The singleton guard itself is present and correct in this frontend (unlike DF-5076's ipmi2_pci). - The bug β
ipmi_acpi_attach(ipmi_acpi.c:104-109): theSSIF_MODEcase reads_ADR, prints "SSIF interface not supported on ACPI", andreturn (0)β attach SUCCESS on a completely uninitialized softc: -sc->ipmi_enqueue_requestis NULL (onlyipmi_kcs_attach(ipmi_kcs.c:487) /ipmi_smic_attach(ipmi_smic.c:393) set it), -lockinit/cv_init/TAILQ_INITnever ran (they live inipmi_startup(ipmi.c:719-721), reached only via the config_intrhook thatipmi_attach()(never called) establishes), - no cdev, no kthread, andipmi_attachedstays 0. - The crash β any detach of that device. Proven on the
kldunloadpath: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_LOCKon 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 bytes0c 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
_LENis one byte. _SRVmust 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.
Fix verification
fixedfix.diff applied to guest /usr/src (module-only rebuild, same kernel, same SSDT): attach now refuses ('ipmi0: SSIF interface not supported on ACPI' + 'device_probe_and_attach: ipmi0 attach returned 6'), no device attached, kldunload returns 0, guest stays alive. Baseline with pristine module in the identical setup panics (trap 12, rip=0). Bad behavior GONE with fix.
fix.diff; VERDICT.md 'Fix validation' section; guest transcript: LOADED_FIXED / attach returned 6 / UNLOAD_RC_0_CLEAN / NO_IPMI_LOADED / STILL_ALIVE
Confirmed kernel references
Detail
Exploit chain
firmware/VM config (IPI0001 + _IFT=4 SSDT) -> ipmi_acpi probe match -> dead-softc attach (return 0) -> module unload/teardown -> NULL indirect call -> fatal trap 12 at address 0 -> DDB (system down). No escalation: target address is fixed NULL, no memory corruption.
Evidence (decisive lines)
panic.txt (decisive serial capture: 'Fatal user address access from kernel mode from kldunload at 0000000000000000' / rip=0x0); run.log (baseline + fix sequence); VERDICT.md (full narrative incl. fix validation transcript); ssdt_gen.py (trigger generator); env.txt (guest uname)
PoC changes
Seed concept replaced entirely: trigger is a hand-assembled SSDT (ssdt_gen.py), not a C program. Required: byte-swapped EISAID (0x01000926), ACPICA 2-byte PkgLength form (byte1<<4 | byte0&0xF), Buffer(N) size TermArg, 1-byte small-IO _LEN, _SRV present; audit-tree kernel build (stock prebuilt guest kernel ignores RSDT-SSDT devices); trigger via kldunload (plain reboot does not tear the module down).
Verified recommended fix
ipmi_acpi.c SSIF case: return ENXIO instead of return(0) (never attach a dead softc); plus defensive NULL guard on ipmi_enqueue_request in ipmi_submit_driver_request (see fix.diff).
Verdict
REPRODUCED on the audit-tree kernel+module: an ACPI IPI0001 device with _IFT=4 (SSIF) makes ipmi_acpi_attach() return 0 on a completely uninitialized softc (ipmi_acpi.c:104-109 β no ipmi_enqueue_request, no lockinit, no cdev). kldunload then runs ipmi_detach -> ipmi_set_watchdog(sc,0) (ipmi.c:887, DragonFly-specific) -> ipmi_submit_driver_request -> sc->ipmi_enqueue_request(sc,req) == NULL call: 'Fatal user address access from kernel mode from kldunload at 0000000000000000', trap 12, rip=0, DDB, guest dead (panic.txt). Fix validated in the same setup: with fix.diff the attach refuses with ENXIO ('attach returned 6') and kldunload completes cleanly (UNLOAD_RC_0_CLEAN). Firmware/config-gated availability impact only (CWE-476); no attacker control of the call target.
No comments yet.