# DF-5076 — `ipmi2_pci_probe` is the only IPMI frontend probe lacking the `ipmi_attached` re-attach guard → double attach of the IPMI stack on dual-description hardware

File: `sys/dev/misc/ipmi/ipmi_pci.c` (pass 2, GLM 5.3)
Severity: Low · CWE-667 (Improper Locking)/CWE-362-adjacent (unguarded
singleton invariant) · confidence: certain (code fact); impact hardware-topology-gated
Status: `untested` (no PCI IPMI device exists on the QEMU guest — QEMU has no
native-PCI IPMI device model at all; env.txt).

## 1. The singleton invariant and the family that enforces it

The ipmi stack is a singleton: `ipmi.c:65` declares `int ipmi_attached = 0;`
and `ipmi_attach` sets `ipmi_attached = 1` on every successful attach
(`ipmi.c:864`). All *other* frontend probes enforce it before claiming a
device:

- `ipmi_pci.c:77` — `ipmi_pci_probe` (Dell PE2650 1028:000d quirk): guarded
- `ipmi_isa.c:85` — `ipmi_isa_probe` (SMBIOS/ISA): guarded
- `ipmi_acpi.c:62` — `ipmi_acpi_probe` (ACPI namespace): guarded

`ipmi2_pci_probe` — the **native PCI IPMI** frontend (class 0c:07,
progif SMIC=0/KCS=1/BT=2 per pcireg.h:414-416) — has **no guard**
(`ipmi_pci.c:183-193`). It matches *any* PCI function with
class=0x0c subclass=0x07 regardless of whether another frontend already
attached the (same or another) BMC interface.

## 2. Consequence chain on dual-description hardware

On a board whose BMC is reachable both through a legacy description (SMBIOS
type 35 → isa; ACPI PNP node; or the Dell PCI quirk dev) *and* as a native
PCI IPMI function — the exact topology the three sibling guards exist for —
newbus can finish `ipmi_attach()` on the first frontend (setting
`ipmi_attached=1`), and the unguarded `ipmi2_pci_probe` still claims the
native PCI function:

1. `ipmi2_pci_attach` (`ipmi_pci.c:196-275`) maps the native BAR(0)
   (:223-231), dispatches on progif (KCS → `ipmi_kcs_probe_align`
   `:249`, `ipmi_kcs_attach` `:255`; SMIC → `ipmi_smic_attach` `:262`),
   then calls `ipmi_attach(dev)` (`:267`) again:
2. second `config_intrhook_establish` → second `ipmi_startup` run
   (`ipmi.c:708`) → second `make_dev("ipmi%d")` (`ipmi.c:831`, unit 1) and a
   **second kthread** (`ipmi_kcs.c:476` / `ipmi_smic.c:382`) —
3. when both descriptions point at the *same* BMC (the historical reason the
   guards were added — Dell PE2650-era machines exposed the BMC both via
   SMBIOS/ISA and the 1028:000d PCI function), two kcs/smic state machines
   now interleave transactions on one KCS/SMIC interface. Interleaving
   corrupts the KCS/SMIC handshake, and with this driver's *unbounded*
   reply/error polling loops (DF-1865 `ipmi_kcs.c`, DF-1866 probe_align,
   DF-2003 `ipmi_smic.c`) the realistic ceiling is a stuck, CPU-pinning
   kernel thread — a boot-time or runtime hang, i.e. local/DoS-class impact,
   not memory corruption.

FreeBSD inherits the identical omission (verified against FreeBSD HEAD
`sys/dev/ipmi/ipmi_pci.c`: `ipmi_pci_probe` guarded, `ipmi2_pci_probe` not),
so this is an upstream-inherited family inconsistency worth reporting to
both projects.

## 3. Verification performed

- Static proof: `guard_matrix.log` — grep of all four probes.
- Guest: **not testable** — the QEMU guest has no class-0c:07 device
  (`pciconf -l` in env.txt shows no serial-bus IPMI function), and QEMU
  provides no PCI IPMI device model to instantiate one; the current guest
  config doesn't even attach ipmi (no /dev/ipmi*, no dmesg lines — the
  DF-4777/4948/4978 era used BMC-sim frontends that are not in this boot's
  QEMU invocation). The defect is a two-line code fact provable by
  inspection; `untested` matches the DF-4985/DF-5000 precedent.
- `fix.diff` (add the sibling guard) passes `git apply --check`; never
  applied to `sys/`.

## 4. Negative results (everything else hunted in ipmi_pci.c and killed)

- **iftype dispatch**: `ipmi_pci_attach` first switch (`:103-116`) fully
  partitions iface_type (KCS/SMIC proceed, BT/SSIF/unknown → ENXIO); the
  second switch (`:142-153`) can only see KCS|SMIC, so `error` is always set
  before use — no uninitialized-`error` path. `ipmi2_pci_attach` dispatches
  on progif with explicit default → ENXIO (`:205-220`); SSIF (progif 0x03)
  correctly refused.
- **attach unwind**: both `bad:` paths (`:159-161`, `:272-274`) call
  `ipmi_release_resources`, which is idempotent-safe here (single call per
  failure; irq/io pointers only released once). `ipmi_kcs_attach`
  (ipmi_kcs.c:481-503) and `ipmi_smic_attach` (ipmi_smic.c:387-400) only set
  function pointers + one status read — **no allocations, nothing to leak**.
  `ipmi_attach`'s two failure modes (bus_setup_intr ipmi.c:847, hook establish
  :859) fail before creating cdev/kthread — nothing dangling. All early
  ENXIO returns (`:98,112,115,134`) precede any resource allocation. Clean.
- **detach**: wired to shared `ipmi_detach` (`:168,281`); it refuses while
  handles are open (`ipmi.c:876-880`), destroys cdev, drains kthread, then
  releases resources — no double-free via this glue. (The early-boot
  detach-vs-config_intrhook window — `ipmi_detach` never calls
  `config_intrhook_disestablish` if the hook hasn't fired — is an `ipmi.c`
  lifetime issue, practically unreachable: the window closes during autoconf
  before any agent that could trigger a detach exists. Noted here for the
  next `ipmi.c` pass; not filed on this file.)
- **BARs**: only BAR(0) mapped; `PCI_BAR_IO` type check (`:224`) correct;
  rman does not bounds-check bus_space reads, but SMBIOS `offset`→spacing is
  properly decoded to {1,2,4} (ipmi_smbios.c:112-118) and native spacing is
  1/re-probed — no kernel memory-safety exposure (worst case: stray in-BAR
  MMIO reads on mis-describing hardware).
- `ipmi_pci_match` (`:62-70`): pure table lookup, terminator-guarded loop.
- SMIC native path uses fixed spacing 1 without re-probe (`:229`) — matches
  upstream FreeBSD; no DF divergence.

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