# VERDICT — DF-5203: ipmi_smbus_probe missing `ipmi_attached` singleton guard

**Status: untested (runtime construction impossible on this guest) — defect certain as a static code fact.**
**Impact ceiling: DoS-class double attach (second kthread + second cdev + interleaved SSIF state machines), Low severity, hardware/firmware-gated.**

## 1. Root cause

The IPMI driver enforces a driver-wide singleton through the global
`int ipmi_attached` (`sys/dev/misc/ipmi/ipmi.c:65`), which every successful
`ipmi_attach()` sets to 1 (`ipmi.c:864`) — and which `ipmi_attach()` itself never
checks. The check lives in the *frontend probes*:

| frontend | probe | guard |
|---|---|---|
| ipmi_pci (Dell 1028:000d quirk) | `ipmi_pci.c:77` | `if (ipmi_attached) return (ENXIO);` |
| ipmi_isa | `ipmi_isa.c:85` | `if (ipmi_attached) return (ENXIO);` |
| ipmi_acpi | `ipmi_acpi.c:62` | `if (ipmi_attached) return (EBUSY);` |
| ipmi2_pci (native PCI 0c:07) | `ipmi_pci.c:183-193` | **none — DF-5076** |
| **ipmi_smbus (this finding)** | `ipmi_smbus.c:65-71` | **none — this finding** |

`ipmi_smbus_probe()` (ipmi_smbus.c:65-71) unconditionally claims any `ipmi` child
on an smbus bus: it sets the device description and returns `BUS_PROBE_DEFAULT`
with no state check whatsoever (`guard_matrix.log`).

Note: DF-5076's record states ipmi2_pci_probe is "the only" unguarded probe — that
claim is factually incomplete; this is the second instance, in a different file.

`ipmi_smbus_identify()` (ipmi_smbus.c:55-63) adds an `ipmi` child per smbus bus
whose only dedup is `device_find_child(parent, "ipmi", -1) == NULL` — **per-parent**,
not global: with ≥2 smbus buses (e.g. ichsmb0 + iicsmb0) each bus gets its own
child, and both probes claim them.

## 2. Double-attach chain (what the missing guard permits)

Preconditions (all firmware-set at boot, no runtime attacker control):
SMBIOS type-38 record with `iface_type == SSIF_MODE`, **and** either
(a) ≥2 smbus buses in the system, or (b) an ACPI `IPI0001` device with `_IFT=SSIF
(the DF-5145 stub attaches a dead softc *without* setting `ipmi_attached`, so it
does not block a later smbus attach either — `ipmi_acpi.c:104-109` returns 0 with
nothing initialized).

1. Boot order: smbus0's child attaches → `ipmi_smbus_attach` (ipmi_smbus.c:73-112):
   `ipmi_ssif_attach` (ipmi_ssif.c:361-374 — stores bus+address, returns 0
   unconditionally) then `ipmi_attach` (ipmi.c:841-866, no check) →
   `ipmi_attached = 1`, config_intrhook established.
2. smbus1's child probes: **unguarded probe returns BUS_PROBE_DEFAULT**, attach
   re-checks only `info.iface_type == SSIF_MODE` (ipmi_smbus.c:84) — which is a
   property of the same SMBIOS record, still true → second `ipmi_attach`.
3. Consequences: second intrhook → second `ssif_startup` kthread (`ssif_loop`,
   ipmi_ssif.c:314-351), second `/dev/ipmiN` cdev (ipmi.c:831), two watchdog
   callouts each arming the same BMC watchdog, and two `ssif_loop` threads issuing
   interleaved multi-part SMBus block transactions (WRITE_START/WRITE_CONT/READ_*)
   to the same BMC address. The ceiling is a wedged/DoS'd BMC interface and confused
   watchdog state — not kernel memory corruption (each softc is independent and
   correctly locked; the corruption-free interleaving analysis mirrors DF-5076's).

## 3. Why untested (not `not_reproduced`)

The topology cannot be constructed on the audit guest (`env.txt`):
- `pciconf -l`: no class 0c:05 device exists (PIIX4 PM function shows as
  class 0x068000, unclaimed) → **zero smbus buses**, so `ipmi_smbus_identify`
  never even runs.
- No SMBIOS type-38 record: QEMU `-smbios` supports only types 0,1,2,3,4,17; type
  38 cannot be injected via the monitor either. Without it
  `ipmi_smbios_identify()` returns 0 and no child is created.
- Live demonstration: `kldload ipmi` (module includes ipmi_smbus.c, built per
  `sys/dev/misc/ipmi/Makefile` SRCS and `sys/conf/files:1054
  dev/misc/ipmi/ipmi_smbus.c optional ipmi smbus`) → nothing attaches, no
  `/dev/ipmi*`, `sysctl dev.ipmi` unknown; `kldunload ipmi` clean. Guest left
  clean (`guest_dirty=0`).

This matches the verification posture of the sibling DF-5076 (untested, static
proof), which faced the same hardware gating.

## 4. Negative results (everything else hunted in this file, killed)

- **identify**: re-parses SMBIOS per smbus bus — a repeated-walk cost only; the
  walk itself is `ipmi_smbios.c` (audited clean; DF-2107 known). No lifetime issue.
- **attach unwind (b/d)**: `ipmi_ssif_attach` allocates nothing and cannot fail;
  the only reachable post-ssif failure is `ipmi_attach` (irq setup is skipped —
  `sc->ipmi_intr` is never set for SSIF — leaving only
  `config_intrhook_establish` ENOMEM, effectively unreachable). On `bad:` the
  single `ipmi_release_resources(dev)` (ipmi_smbus.c:110) releases everything
  this frontend allocated (the optional IRQ resource, ipmi_smbus.c:91-96).
  Complete and single-shot; no leak, no half-initialized kthread (kthread is only
  created later by the intrhook, ipmi.c:724 → ipmi_ssif.c:357).
- **address truncation (b/c)**: `info.address` is `uint64_t` (ipmivars.h:34) from
  SMBIOS, passed as `int` to `ipmi_ssif_attach` (ipmivars.h:245). A 7-bit SMBus
  address cannot lose bits in the narrowing; a corrupt record yields a wrong
  slave address on the wire (robustness), not kernel memory unsafety —
  `smbus_bread/bwrite` take the value as a driver address, bounds are enforced in
  the smbus core. No finding.
- **iface dispatch (c)**: the only type dispatch is `info.iface_type != SSIF_MODE
  → ENXIO` (ipmi_smbus.c:84-87); all paths partition; no uninitialized `error` use.
- **detach (d)**: glue is the shared guarded `ipmi_detach` (ipmi.c:869-904) —
  kthread drain via `ipmi_detaching` + `cv_broadcast` + `lksleep`
  (ipmi_ssif.c:314-351 loop exits via `ipmi_dequeue_request` NULL,
  ipmi.c:612-615). The detach-before-intrhook window (lock/cv initialized in
  `ipmi_startup`, ipmi.c:719-720) is a shared ipmi.c lifetime property common to
  all frontends, already noted in DF-5076's VERDICT for the next ipmi.c pass, and
  practically unreachable (module unload is refused while children live).
- **build (e)**: compiled into every `ipmi.ko` build (Makefile SRCS,
  sys/conf/files:1054) and into kernels with `device ipmi` + `device smbus` —
  live code, not dead, which is why the guard matters.

## 5. Fix

`fix.diff` (git apply --check verified against pristine `sys/`, never applied):
mirror the sibling guard in `ipmi_smbus_probe`:

```diff
+	if (ipmi_attached)
+		return (ENXIO);
```

One-line, identical shape to ipmi_pci.c:77-78 / ipmi_isa.c:85-86. FreeBSD HEAD
carries the same omission in its sys/dev/ipmi/ipmi_smbus.c — fix applies to both.

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