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)
PoC verification
Evidence pack
findings/poc/DF-5203 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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-filedipmi2_pci_probe, DF-5076). - On firmware with SMBIOS type-38 = SSIF and β₯2 smbus buses, two
ipmidevices 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, livekldload ipmidemo showing identify is a no-op without the firmware recordfix.diffβ adds the sibling guard toipmi_smbus_probe(git apply --check OK)VERDICT.md,manifest.json,verdict.json
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).
- 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) thenipmi_attach(ipmi.c:841-866, no check) βipmi_attached = 1, config_intrhook established. - 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 β secondipmi_attach. - Consequences: second intrhook β second
ssif_startupkthread (ssif_loop, ipmi_ssif.c:314-351), second/dev/ipmiNcdev (ipmi.c:831), two watchdog callouts each arming the same BMC watchdog, and twossif_loopthreads 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_attachallocates nothing and cannot fail; the only reachable post-ssif failure isipmi_attach(irq setup is skipped βsc->ipmi_intris never set for SSIF β leaving onlyconfig_intrhook_establishENOMEM, effectively unreachable). Onbad:the singleipmi_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.addressisuint64_t(ipmivars.h:34) from SMBIOS, passed asinttoipmi_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/bwritetake 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 uninitializederroruse. - detach (d): glue is the shared guarded
ipmi_detach(ipmi.c:869-904) β kthread drain viaipmi_detaching+cv_broadcast+lksleep(ipmi_ssif.c:314-351 loop exits viaipmi_dequeue_requestNULL, ipmi.c:612-615). The detach-before-intrhook window (lock/cv initialized inipmi_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.kobuild (Makefile SRCS, sys/conf/files:1054) and into kernels withdevice 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_testablenot_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/)
Confirmed kernel references
- sys/dev/misc/ipmi/ipmi_smbus.c:65-71
- sys/dev/misc/ipmi/ipmi_smbus.c:55-63
- sys/dev/misc/ipmi/ipmi_smbus.c:73-112
- sys/dev/misc/ipmi/ipmi_pci.c:77-78
- sys/dev/misc/ipmi/ipmi_isa.c:85-86
- sys/dev/misc/ipmi/ipmi_acpi.c:62-63
- sys/dev/misc/ipmi/ipmi_acpi.c:104-109
- sys/dev/misc/ipmi/ipmi.c:65
- sys/dev/misc/ipmi/ipmi.c:841-866
- sys/dev/misc/ipmi/ipmi.c:831
- sys/dev/misc/ipmi/ipmi_ssif.c:314-351
- sys/dev/misc/ipmi/ipmi_ssif.c:353-359
- sys/dev/misc/ipmi/ipmi_ssif.c:361-374
- sys/conf/files:1054
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).
No comments yet.