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

ipmi_smbus_probe is a second unguarded probe in the ipmi_attached singleton family (DF-5076 twin) β€” cross-bus/dual-smbus double attach yields second ssif kthread, second /dev/ipmiN cdev, and interleaved SSIF state machines on one BMC

Summary

DF-5076's 'only unguarded probe' claim was incomplete: the smbus probe claims unconditionally and its identify dedup is per-parent - dual-smbus or the DF-5145 stub (which attaches without setting ipmi_attached) yields two kthreads/two cdevs/two watchdogs interleaving SSIF transactions on one BMC. Firmware-topology-gated (no runtime attacker control). Sibling-guard fix (apply-verified).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5203 Β· 7 files
FileTypeDescriptionSize
guard_matrix.log β€” 2.8 KB view raw
env.txt β€” 2.3 KB view raw
fix.diff β€” 391 B view raw
VERDICT.md β€” 6.9 KB ↓ raw
README.md β€” 1.7 KB ↓ raw
manifest.json β€” 1.4 KB view raw
verdict.json β€” 5.7 KB view raw

DF-5203 β€” ipmi_smbus_probe lacks the ipmi_attached singleton guard (DF-5076 family, 2nd instance)

  • File: sys/dev/misc/ipmi/ipmi_smbus.c:65-71
  • Severity: Low (hardware/firmware-gated double attach; DoS-class ceiling)
  • Bucket: base:dev
  • Confidence: certain (guard absence is a static code fact; matrix in guard_matrix.log)

Build / run (reproduce the static proof β€” no runtime PoC exists)

# guard matrix across all four IPMI frontends:
grep -n ipmi_attached sys/dev/misc/ipmi/ipmi_pci.c sys/dev/misc/ipmi/ipmi_isa.c \
                       sys/dev/misc/ipmi/ipmi_acpi.c sys/dev/misc/ipmi/ipmi.c
#   -> ipmi_pci.c:77, ipmi_isa.c:85, ipmi_acpi.c:62  (guards)
grep -c ipmi_attached sys/dev/misc/ipmi/ipmi_smbus.c
#   -> 0                                            (missing guard)

# fix validation (against pristine tree; NEVER applied):
git apply --check findings/poc/DF-5203/fix.diff   # passes

Expected

  • Guard present in 3 sibling probes, absent in ipmi_smbus_probe (and in the separately-filed ipmi2_pci_probe, DF-5076).
  • On firmware with SMBIOS type-38 = SSIF and β‰₯2 smbus buses, two ipmi devices attach: second kthread (ssif_loop), second /dev/ipmiN, two watchdog callouts arming the same BMC watchdog, interleaved multi-part SMBus block transactions to one BMC address.

Artifacts

  • guard_matrix.log β€” the decisive static proof (grep matrix + consequence chain)
  • env.txt β€” guest runtime evidence: no smbus bus, no SMBIOS type-38, live kldload ipmi demo showing identify is a no-op without the firmware record
  • fix.diff β€” adds the sibling guard to ipmi_smbus_probe (git apply --check OK)
  • VERDICT.md, manifest.json, verdict.json
VERDICT.md
↓ download raw

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* settingipmi_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:

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

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable - requires SMBIOS type-38 SSIF firmware plus >=2 smbus buses (or the ACPI IPI0001 SSIF stub); the audit guest has no smbus bus at all and QEMU cannot supply type-38 SMBIOS records. fix.diff is git-apply --check verified against the pristine sys/ tree and mirrors the three in-tree sibling guards exactly; logically airtight for a guard-insertion fix.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

guard_matrix.log (guard at ipmi_pci.c:77 / ipmi_isa.c:85 / ipmi_acpi.c:62, count 0 in ipmi_smbus.c; consequence chain to 2nd kthread/cdev); VERDICT.md sections 1-3; env.txt (guest: no 0c:05 device, no SMBIOS type-38, live kldload ipmi no-op demo); fix.diff (git apply --check passes, never applied)

PoC changes

evidence pack authored fresh in pass 2 (no seed). No runtime PoC exists or is constructible: the double attach requires SMBIOS type-38 SSIF firmware plus a second smbus bus; the audit guest has neither (no 0c:05 PCI function, QEMU cannot inject type 38). Live kldload ipmi probe performed instead to prove identify is a no-op without the firmware record.

Verified recommended fix

Add the singleton guard to ipmi_smbus_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 SMBus frontend (the 4th transport glue). The driver enforces a driver-wide singleton via the global ipmi_attached (ipmi.c:65, set to 1 by every successful ipmi_attach at ipmi.c:864 and never checked inside ipmi_attach itself); the check lives in the frontend probes -- ipmi_pci_probe honors it (ipmi_pci.c:77), ipmi_isa_probe (ipmi_isa.c:85), ipmi_acpi_probe (ipmi_acpi.c:62), and DF-5076 already filed the unguarded ipmi2_pci_probe (ipmi_pci.c:183-193) claiming to be the only omission. It is not: ipmi_smbus_probe (ipmi_smbus.c:65-71) also unconditionally claims any 'ipmi' child on an smbus bus, and ipmi_smbus_identify's dedup (ipmi_smbus.c:61) is per-parent only (device_find_child on that smbus bus), not global. On firmware whose SMBIOS type-38 record says SSIF and which exposes >=2 smbus buses (or an ACPI IPI0001 IFT=SSIF stub -- which per DF-5145 attaches a dead softc WITHOUT setting ipmi_attached, so it blocks nothing), each smbus bus gets an ipmi child, each unguarded probe claims it, and each attach (ipmi_smbus.c:73-112; the only gate is the same SMBIOS record's iface_type==SSIF at :84) runs ipmi_ssif_attach + ipmi_attach again: second config_intrhook, second ssif_loop kthread (ipmi_ssif.c:314-351,357), second /dev/ipmiN cdev (ipmi.c:831), two watchdog callouts arming the same BMC watchdog, and two threads issuing interleaved multi-part SMBus block transactions (WRITE_START/WRITE_CONT/READ*) to one BMC address. Ceiling is wedged-BMC DoS and confused watchdog state, not memory corruption (softcs are independent and locked; identical analysis to DF-5076). Severity Low: purely firmware-gated topology, no runtime attacker control. Untestable on this guest by construction (env.txt): pciconf shows no class 0c:05 device so zero smbus buses exist, there is no SMBIOS type-38 record and QEMU cannot inject one (-smbios covers types 0,1,2,3,4,17 only); live kldload ipmi demonstration attached nothing and was unloaded, guest left clean. Static proof in guard_matrix.log; fix.diff adds the sibling guard (git apply --check verified, never applied to sys/). FreeBSD HEAD carries the identical omission. Negative results (VERDICT.md section 4): identify/attach unwind complete and single-shot (ipmi_ssif_attach allocates nothing and cannot fail; only effectively-unreachable post-ssif failure is config_intrhook ENOMEM; ipmi_release_resources covers everything this frontend holds), uint64->int address narrowing cannot corrupt the 7-bit SMBus address and lands in the bounds-checked smbus core, iface dispatch fully partitions, detach glue is the shared guarded ipmi_detach with proper ssif kthread drain, and the file is live code in every ipmi.ko build (sys/conf/files:1054, Makefile SRCS). Known family findings NOT re-reported: DF-4777 (kcs), DF-4948 (smic), DF-4978 (ssif replylen), DF-5076 (ipmi2_pci guard), DF-5145 (acpi stub), DF-2107 (smbios walk).