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

ichsmb_pci.c:175 wires device_detach to ichsmb_detach, whose device_delete_child() return is ignored (ichsmb.c:689) before unconditional IRQ/IO teardown, lockuninit and a lying success return β€” DF-5544/5545/5562 family residue, with every deterministic veto path caught one level up (live-proven)

Summary

The PCI glue registers ichsmb_detach, which discards device_delete_child's return then unconditionally releases resources + lockuninit + returns 0 - the family teardown-under-veto shape. UNLIKE uhci/xhci/ohci it is NOT deterministically triggerable today: ichsmb_detach checks bus_generic_detach first, smbus_detach checks its own cascade, smb_detach never vetoes, and second-phase detach no-ops. Live-proven on the q35 rig: veto child -> kldunload fails 'Device busy' with irq+IO fully intact (contrast DF-5544's swallowed veto + freed softc + trap 12); noted collateral - the vetoed unwind still detaches smb0/smb1 and removes /dev/smb* (generic non-transactional detach). Residual: root-vs-root race or a future vetoing smbus driver. check+propagate fix mirrors the family fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5598 Β· 11 files
FileTypeDescriptionSize
veto.c β€” 3.9 KB view raw
Makefile β€” 98 B ↓ download
build.sh β€” 138 B view raw
build.log β€” 1002 B view raw
run.sh β€” 144 B view raw
run.log β€” 2.1 KB view raw
env.txt β€” 675 B view raw
fix.diff β€” 332 B view raw
VERDICT.md β€” 5.8 KB ↓ raw
README.md β€” 2.5 KB ↓ raw
verdict.json β€” 4.6 KB view raw

DF-5598 β€” ichsmb detach-veto family residue (Info, hardening)

File: sys/bus/smbus/ichsmb/ichsmb_pci.c (pass 2) Class: detach-veto family (DF-5544 / DF-5545 / DF-5562 / DF-5528 / DF-4558) β€” mitigated variant

What this pack demonstrates

ichsmb_pci.c:175 wires device_detach to ichsmb_detach() (ichsmb.c:680), whose device_delete_child(dev, sc->smb) return at ichsmb.c:689 is ignored before the unconditional ichsmb_release_resources() + lockuninit() + return 0. This is the family shape β€” but unlike uhci/xhci/ohci, ichsmb_detach first runs a checked bus_generic_detach(dev) cascade (ichsmb.c:686) and smbus_detach() checks its own cascade (smbus.c:110-112).

This harness attaches a vetoing child (EBUSY) directly under smbus0 (grandchild of ichsmb0) and drives the exact kldunload path (linker_file_unload -> devclass_delete_driver, subr_bus.c:897).

Build (inside the guest)

sh build.sh        # make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC

Requires: q35 rig guest (-machine q35 -device ipmi-bmc-sim,id=bmc0 -device smbus-ipmi,bmc=bmc0,address=0x20 -device i2c-ddc,address=0x50), kldload ichsmb, kldload smb.

Run (as root, inside the guest)

sh run.sh

Expected output

  • kldunload ichsmb fails (Device busy) while the veto child is armed: df5598veto0: DETACH VETO: returning EBUSY on the console, and devinfo -r still shows ichsmb0 with I/O ports 0x700-0x73f and irq 16 β€” the veto is HONORED, nothing is torn down, no panic. (This is the opposite of DF-5544, where the same trigger produced uhci0: detached + return 0 + freed softc + fatal page fault.)
  • Collateral observed: smb0/smb1 are detached (their /dev/smb* nodes removed) before the veto surfaces β€” a vetoed unload leaves the SMBus service dead until a successful retry (generic newbus non-transactional detach; robustness note, not memory corruption).
  • After sysctl hw.df5598_armed=0, kldunload ichsmb returns 0 and the whole subtree (df5598veto0, smbus0, ichsmb0) detaches cleanly.

Artifacts

  • veto.c, Makefile β€” the KLD stand-in veto child (adapted from the DF-5544 harness; the kernel thread is unnecessary because kldunload is a stock syscall-level trigger here).
  • build.sh, build.log β€” build transcript.
  • run.sh, run.log β€” full decisive run.
  • fix.diff β€” check + propagate device_delete_child() (hardening).
  • env.txt, VERDICT.md, manifest.json, verdict.json.
VERDICT.md
↓ download raw

DF-5598 VERDICT β€” detach-veto family residue on ichsmb: NOT the uhci bug

Bottom line

not_reproduced / impact none / certain. The DF-5544/5545/5562 family failure (deterministic teardown-under-veto) does not manifest on the ichsmb PCI glue: every deterministic veto in the subtree is caught one level up by the checked bus_generic_detach() cascade, so ichsmb_release_resources() never runs under a vetoing child. The ignored device_delete_child() return at ichsmb.c:689 (reached via the DEVMETHOD(device_detach, ichsmb_detach) wiring at ichsmb_pci.c:175) is a genuine newbus-contract violation and is kept as an Info defense-in-depth finding consistent with the family sweep.

Why the family bug is unreachable here (code)

  1. ichsmb_detach (ichsmb.c:686-688) checks bus_generic_detach(dev) and returns the error before any teardown. bus_generic_detach (subr_bus.c:2867-2880) detaches all children, cascading.
  2. The only child is smbus0 (ichsmb.c:114); its smbus_detach (smbus.c:106-115) checks its own bus_generic_detach over the smb0..smbN (and any other) children.
  3. The in-tree leaf smb_detach (smb.c:147-155) never vetoes β€” it removes the devfs node and returns 0 unconditionally, even with /dev/smbN open (mode 0600 root anyway).
  4. After a successful bus_generic_detach, the second-phase device_detach(child) inside device_delete_child (subr_bus.c:1292) is a no-op (state != DS_ATTACHED -> return 0, subr_bus.c:2136-2137), so no in-tree veto can surface there.
  5. The DS_BUSY early-EBUSY (subr_bus.c:2134-2135) requires device_busy() on an smbus-subtree device; the only callers in the tree are cxm/bktr/agp/drm/fd β€” none in sys/bus/smbus or sys/dev/smbus.

Residual exposure of the unchecked call (ichsmb.c:689): * a driver kldloaded onto the smbus bus concurrently with the kldunload (root-vs-root race) that attaches a child between the checked generic detach and device_delete_child β€” its veto would then be swallowed and ichsmb_release_resources() + lockuninit() would run under it while device_detach(ichsmb0) reports success; * any future smbus-bus driver whose detach vetoes only in the second phase (none possible today, per 4).

Live evidence (q35 + ipmi-bmc-sim rig, ICH9 82801I at 0:31:3)

run.log β€” decisive run:

kldunload ichsmb -> "kldunload: can't unload file: Device busy" (rc=1)
df5598veto0: DETACH VETO: returning EBUSY ...
>>> ichsmb0 still attached with resources: veto HONORED
  ichsmb0 / Interrupt request lines cpu4: 16 / I/O ports 0x700-0x73f   (intact)
sysctl hw.df5598_armed=0
kldunload ichsmb -> rc=0, df5598veto0/smbus0/ichsmb0 detach cleanly

Serial (dmesg) confirms the unwind order: smb1: detached -> smb0: detached -> df5598veto0: DETACH VETO -> (unload aborts, resources intact), then after disarming: df5598veto0: cooperating -> detached -> smbus0: detached -> ichsmb0: detached.

Contrast with DF-5544 (same harness pattern, uhci): there the trigger yielded uhci0: detached, return 0, freed softc and a Fatal trap 12 page fault. Here: honest EBUSY, zero teardown, guest healthy.

Observed collateral (noted, not filed)

The vetoed unwind still detaches smb0/smb1 before the veto surfaces (their /dev/smb* devfs nodes are destroyed β€” verified by the failed smbmsg checks in run.log), so a vetoed kldunload ichsmb leaves the SMBus userland interface dead until a successful retry, even though the controller keeps its IRQ/IO. This is generic newbus non-transactional detach semantics (the same "retry later" posture DF-5544's fix embraces), not a memory-safety defect, and it is not re-filed separately.

Fix

fix.diff β€” propagate device_delete_child()'s error before touching resources (3 lines, mirrors the family fix). Not kernel-rebuild-validated: there is no reproducible bad behavior to A/B on this rig (that is the point of the finding), so fix_status=not_testable; the diff is trivially reviewable and matches the validated DF-5544/5545/5562 fix shape.

Negative results recorded during this pass (pass-2 depth)

  • Probe table walk bounded/NULL-terminated (ichsmb_pci.c:210-215); duplicate ICH7/8/9 rows (:121-126) are cosmetic first-match-wins.
  • No revision/quirk dispatch exists (checklist item c): DID-only match at ichsmb_pci.c:211 β€” nothing attacker-shaped to reach.
  • ICH_HOSTC byte write at ichsmb_pci.c:255 clobbers SMB_SMI_EN / I2C_EN (ichsmb_reg.h:53-56) β€” firmware-config destruction only; no attacker control, no kernel-memory consequence. It also happens before bus_setup_intr (ichsmb.c:124), but no SMBus transaction source is armed before smbus_attach's probing (smbus.c:94-100) which runs after the handler install β€” window benign. HOSTC is neither restored on attach failure nor disabled on detach (no shutdown method, ichsmb_pci.c:171-193): controller left HST_EN with IRQ released after unload β€” real-HW robustness smell (unhandled SMBALERT# level IRQ), Info-grade, recorded here rather than filed.
  • Attach error unwinding at the PCI level (ichsmb_pci.c:258-266) is sound; the deeper ichsmb_attach unwinding bugs are DF-5460's (not re-reported).
  • RF_SHAREABLE IRQ (ichsmb_pci.c:246-247) with a spurious-safe handler (ichsmb.c:509-518) β€” OK.
  • No copyin/copyout, no lengths, no locks in this file.
  • bus_generic_detach's TAILQ_FOREACH vs concurrent device_add_child (subr_bus.c:2875) β€” generic newbus root-vs-root race, not file-specific.

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

No reproducible bad behavior exists to A/B (the finding's substance is that the deterministic family failure does NOT reach the teardown); fix.diff is the trivial 3-line check+propagate identical in shape to the already-validated DF-5544/5545/5562 fixes and is supplied for review completeness.

fix.diff; family precedent run.fix.log in findings/poc/DF-5544/
↓ fix.diffper-fix-DF-5598

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (kldunload rc=1 'Device busy', devinfo -r shows ichsmb0 irq 16 + 0x700-0x73f intact after the vetoed attempt, clean rc=0 after hw.df5598_armed=0); dmesg tail in run.log (smb1/smb0 detached -> 'df5598veto0: DETACH VETO: returning EBUSY' -> abort; then 'cooperating -> detached -> smbus0: detached -> ichsmb0: detached'); veto.c/build.sh/run.sh harness; VERDICT.md for the full code-level veto-propagation analysis

PoC changes

Adapted the proven DF-5544 veto harness: (1) parent bus is smbus (child = grandchild of ichsmb0) instead of uhci; (2) no kernel thread needed -- stock 'kldunload ichsmb' is the devclass_delete_driver() trigger (the DF-5544 thread existed only to dodge the sysctl-lock deadlock of a direct device_detach() call); (3) -Werror fixes: dropped volatile on the sysctl-backed int, added for the MODULE_DEPEND version macros.

Verified recommended fix

Check and propagate device_delete_child()'s return at ichsmb.c:689 before ichsmb_release_resources(), so a second-phase child-deletion failure leaves IRQ/IO/softc fully intact and the caller retries (matches the validated DF-5544/5545/5562 family fix).

Verdict

ichsmb_pci.c:175 wires device_detach to ichsmb_detach(), which ignores device_delete_child()'s return (ichsmb.c:689) before the unconditional ichsmb_release_resources() + lockuninit() + 'return 0' -- the DF-5544/5545/5562 family shape. But unlike uhci/xhci/ohci, every deterministic veto in the ichsmb subtree is caught one level up: ichsmb_detach CHECKS bus_generic_detach() (ichsmb.c:686) and smbus_detach checks its own cascade (smbus.c:110-112), the only leaf smb_detach() never vetoes (smb.c:147-155), and the second-phase device_detach inside device_delete_child is a no-op for already-detached devices (subr_bus.c:2136-2137; DS_BUSY dead -- no device_busy() callers in the smbus subtree). Proven live on the q35+ipmi-bmc-sim rig (real ICH9 82801I): with a KLD veto child (EBUSY) under smbus0, 'kldunload ichsmb' honestly fails 'Device busy', ichsmb0 stays attached with irq 16 + IO 0x700-0x73f fully intact, no panic -- the exact opposite of DF-5544's swallowed veto + freed softc + fatal page fault. After disarming, the unload completes cleanly. Residual exposure of the unchecked call: a driver kldloaded onto the smbus bus racing the kldunload (root-vs-root) between the checked generic detach and device_delete_child, or a future vetoing smbus-bus driver; hence Info hardening, not a reachable memory-safety bug.