β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5076

ipmi2_pci_probe is the only IPMI frontend probe lacking the ipmi_attached singleton guard β€” double attach (second cdev + second kthread, interleaved KCS/SMIC state machines) on dual-description BMC hardware

Summary

All three sibling probes enforce the singleton; the native-PCI probe does not - on dual-description BMC boards the second attach creates a second cdev+kthread interleaving one KCS/SMIC interface (compounding the known unbounded loops into a wedge). FreeBSD HEAD inherits the omission. Hardware-gated (no PCI IPMI device model in QEMU). Sibling-guard one-liner fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5076 Β· 7 files
FileTypeDescriptionSize
README.md β€” 1.3 KB ↓ raw
VERDICT.md β€” 6.2 KB ↓ raw
guard_matrix.log β€” 657 B view raw
env.txt β€” 323 B view raw
fix.diff β€” 663 B view raw
manifest.json β€” 1.3 KB view raw
verdict.json β€” 5.1 KB view raw

DF-5076 evidence pack

What

sys/dev/misc/ipmi/ipmi_pci.c β€” ipmi2_pci_probe (:183-193) is the only IPMI frontend probe without the ipmi_attached singleton guard (siblings: ipmi_pci.c:77, ipmi_isa.c:85, ipmi_acpi.c:62). On boards whose BMC is described twice (SMBIOS/ISA/ACPI/Dell-quirk and native PCI class 0c:07), the native PCI function attaches a second full IPMI stack (second cdev, second kthread at ipmi_kcs.c:476 / ipmi_smic.c:382) after the first frontend already set ipmi_attached=1 (ipmi.c:864) β€” two state machines interleave on one BMC β†’ KCS/SMIC handshake corruption β†’ stuck kernel threads (compounding the known unbounded loops DF-1865/DF-1866/DF-2003). FreeBSD HEAD inherits the same omission.

Reproduce

Not runnable on this guest: QEMU has no PCI IPMI device model; the guest has no class-0c:07 PCI function (env.txt). Static proof in guard_matrix.log (grep of all four probes). Verdict: untested / code-fact-certain, following the DF-4985/DF-5000 precedent for hardware-gated findings.

Contents

  • VERDICT.md β€” narrative, consequence chain, negative results
  • guard_matrix.log β€” static guard matrix across the four frontends
  • env.txt β€” guest evidence (uname, pciconf: no 0c:07, no AMD GPU, ipmi absent)
  • fix.diff β€” add the sibling guard; git apply --check verified
  • manifest.json, verdict.json
VERDICT.md
↓ download raw

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable - requires dual-description BMC hardware (SMBIOS/ACPI/quirk frontend plus a native PCI class-0c:07 function); the audit guest has no such device and QEMU cannot emulate one. fix.diff is git-apply --check verified against the pristine sys/ tree and mirrors the three in-tree sibling guards exactly.

findings/poc/DF-5076/fix.diff (git apply --check passes; never applied to sys/)
↓ fix.diffper-fix-DF-5076

Confirmed kernel references

Detail

Evidence (decisive lines)

guard_matrix.log (guard present at ipmi_pci.c:77 / ipmi_isa.c:85 / ipmi_acpi.c:62, absent in ipmi2_pci_probe ipmi_pci.c:183-193); VERDICT.md section 2 (double-attach chain: ipmi_pci.c:267 -> ipmi.c:864/831 -> ipmi_kcs.c:476); env.txt (guest: no class-0c:07 device, no PCI IPMI model in QEMU); fix.diff (git apply --check passes)

PoC changes

evidence pack authored fresh in pass 2 (no seed). No runtime PoC exists or is possible: the defect requires a PCI function with class 0x0c subclass 0x07, which the audit guest's QEMU cannot provide (no PCI IPMI device model upstream).

Verified recommended fix

Add the singleton guard to ipmi2_pci_probe mirroring the siblings: if (ipmi_attached) return (ENXIO); -- see fix.diff (same fix applies to FreeBSD HEAD).

Verdict

Pass-2 audit of the IPMI PCI glue. The driver enforces a singleton invariant via the global ipmi_attached (ipmi.c:65, set to 1 on every successful ipmi_attach at ipmi.c:864), and all three sibling frontend probes honor it -- ipmi_pci_probe (Dell 1028:000d quirk, ipmi_pci.c:77), ipmi_isa_probe (ipmi_isa.c:85), ipmi_acpi_probe (ipmi_acpi.c:62). The native-PCI frontend ipmi2_pci_probe (ipmi_pci.c:183-193) does NOT: it matches any class-0c:07 PCI function regardless of a prior attach. On boards whose BMC is described both by a legacy frontend (SMBIOS/ISA, ACPI, or the Dell quirk) and as a native PCI IPMI function -- the exact topology the sibling guards were added for -- newbus attaches the native function after the first attach: ipmi2_pci_attach (ipmi_pci.c:196-275) maps BAR(0), runs the KCS/SMIC setup, and calls ipmi_attach again (ipmi_pci.c:267), producing a second config hook, a second /dev/ipmiN cdev (ipmi.c:831) and a second kthread (ipmi_kcs.c:476 / ipmi_smic.c:382). When both descriptions point at the same BMC (the historical Dell PE2650 case), two state machines interleave transactions on one KCS/SMIC interface; combined with the driver's known unbounded polling loops (DF-1865/DF-1866/DF-2003) the realistic ceiling is a wedged, CPU-pinning kernel thread -- DoS-class, not memory corruption. FreeBSD HEAD inherits the identical omission (verified against upstream sys/dev/ipmi/ipmi_pci.c), so the fix applies to both projects. Severity Low: hardware-topology-gated (needs dual-description BMC hardware), impact hang/DoS. Untestable on this guest: QEMU has no PCI IPMI device model and the guest exposes no class-0c:07 function (env.txt); the current guest boot does not even attach ipmi. Static proof in guard_matrix.log; fix.diff adds the sibling guard, git apply --check verified, never applied to sys/. Negative results (VERDICT.md section 4): the iftype/progif dispatches fully partition (no uninitialized error path), both bad: unwinds are complete and single-shot (kcs/smic attach allocate nothing; ipmi_attach fails before cdev/kthread creation), detach glue is the shared guarded ipmi_detach, BAR handling has no kernel-memory exposure, and the SMBIOS offset->spacing decode is proper ({1,2,4}, ipmi_smbios.c:112-118). The early-boot detach-vs-config_intrhook window is an ipmi.c lifetime issue (noted for the next ipmi.c pass, practically unreachable).