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

iicbus_attach unconditionally re-creates the ic/iic/iicsmb trio on every attach: children survive driver teardown, so each detach+re-attach cycle leaks 3 struct bsd_device and mints duplicate iicN/iicsmbN units (duplicate /dev/iicN nodes on one physical bus)

Summary

Unguarded trio creation with no deletion counterpart in detach or devclass_delete_driver: every re-attach of the same bus mints a second full trio - unbounded M_BUS leak and duplicate devclass units. With iic.ko attachable, reproduced /dev/iic0 -> 1 -> 2 -> 4 nodes on ONE iicbus0 (independent per-node sc_addr state, further breaking transfer_gen's nkid!=1 check); stock witness: 2 production cycles grew nchildren 3 -> 9. Root-gated (needs a resident bridge or rescan flow). Guarded-creation fix validated (nchildren constant).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5633 Β· 12 files
FileTypeDescriptionSize
README.md β€” 3.5 KB ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
verdict.json β€” 3.8 KB view raw
manifest.json β€” 852 B view raw
fix.diff β€” 5.5 KB view raw
run_a2.log β€” 1004 B view raw
run_a2_dmesg.txt β€” 2.4 KB view raw
run_b_5559.log β€” 936 B view raw
run_d_fixed.log β€” 1.1 KB view raw
run_d_fixed_dmesg.txt β€” 2.0 KB view raw
build.log β€” 30.8 KB view raw
env.txt β€” 354 B view raw

DF-5633 β€” iicbus_attach unconditionally re-creates the ic/iic/iicsmb trio on every attach β†’ unbounded child leak + duplicate /dev/iicN nodes on one bus

  • File under audit: sys/bus/iicbus/iicbus.c (pass 2)
  • Class: resource leak / device-topology corruption (logic, DoS)
  • Severity: Low (root-gated: any detach+re-attach of an iicbus with a resident bridge)
  • Verdict: REPRODUCED (3β†’9 children in 2 cycles; /dev/iic0..3 on one bus), fix validated

The defect

iicbus_attach (iicbus.c:144-146) creates the well-known rendezvous children unconditionally:

device_add_child(dev, "ic", -1);
device_add_child(dev, "iic", -1);
device_add_child(dev, "iicsmb", -1);

with no device_find_child guard and no deletion counterpart. Children survive teardown of the bus driver: devclass_delete_driver (subr_bus.c:853-899, the kldunload path) detaches iicbus0 but never deletes its FIXEDCLASS children, and iicbus_detach (iicbus.c:156-162) doesn't delete them either. Every later re-attach of the same iicbus device (device_probe_and_attach after a kldload iicbus.ko with a resident bridge, a rescan, or any driver re-registration) runs iicbus_attach again and mints a second complete trio:

  • +3 leaked struct bsd_device (M_BUS kmalloc, each ~0x2c0 bytes) per cycle, unbounded;
  • duplicate devclass units (iic0, iic1, ... β€” each new child is probe/attachable by iic.ko, producing multiple /dev/iicN character devices on ONE physical bus, each with its own sc_addr state β†’ divergent transactions and permanent confusion about bus ownership;
  • the duplicates also break iicbus_transfer_gen's single-child expectation (iiconf.c:365, nkid != 1 β†’ EIO) even further.

In-tree masking: MODULE_DEPEND(iicbb, iicbus) prevents kldunload iicbus.ko while a module bridge is loaded, so the leak needs a bridge that stays resident across the iicbus driver's removal β€” a bridge compiled into the kernel (device iicbb + module iicbus), an out-of-tree bridge without the dependency, or any code that detaches and re-probes the iicbus device (exactly what kldunload/kldload do internally via devclass_delete_driver/devclass_add_driver).

Reproduction (guest, stock modules)

run_a_stock.sh / run_a2_dmesg.txt: /dev/df5632 ioctl driving the production APIs device_detach(iicbus0) + device_probe_and_attach(iicbus0) (the same functions the module handlers call):

[df5632] STAT: nchildren=3                      (ic, iic, iicsmb; iicbus0 attached)
[df5632] CYCLE 0: detach+reattach ok
[df5632] CYCLE 1: detach+reattach ok
[df5632] CYCLE-done: nchildren=9
[df5632] CYCLE-done: child[3] devclass=ic unit=1 ... child[4] iic unit=1 ...
                      child[6] ic unit=2 ... child[8] iicsmb unit=2

run_b_5559.log (iicbus.ko + DF-5559's validated read/write_ivar fix so iic.ko can attach): duplicate char devices on one bus after cycles:

/dev/iic0  (before)
/dev/iic0 /dev/iic1        (after 1 cycle)
/dev/iic0 /dev/iic1 /dev/iic2 /dev/iic3   (after 3 cycles)

Fix (validated)

fix.diff guards the trio with device_find_child(dev, "<name>", -1) == NULL (mirroring the guards iic_identify/iicsmb_identify already have) and routes creation through the corrected BUS_ADD_CHILD. With only iicbus.ko rebuilt (run_d_fixed.log): nchildren stays constant across cycles β€” after 2 cycles with two extra rig children the count is 5 and does not grow (CYCLE-done: nchildren=5, identical before/after).

VERDICT.md
↓ download raw

DF-5633 VERDICT β€” REPRODUCED (unbounded child-trio leak + duplicate /dev/iicN), fix validated

Root cause (path:line)

iicbus_attach (sys/bus/iicbus/iicbus.c:144-146) re-creates the ic/iic/iicsmb rendezvous trio on EVERY attach, unguarded. Children are never deleted by iicbus_detach (iicbus.c:156-162) nor by devclass_delete_driver (sys/kern/subr_bus.c:853-899, the kldunload force-detach path β€” it only detaches devices of the iicbus devclass), so each detach+re-attach cycle of the same iicbus device accumulates another trio of struct bsd_device (M_BUS) and mints duplicate devclass units.

Evidence

  • Stock, run_a2_dmesg.txt: STAT: nchildren=3 β†’ 2 production detach/re-attach cycles (device_detach + device_probe_and_attach, exactly what kldunload/kldload invoke) β†’ CYCLE-done: nchildren=9 with ic/iic/iicsmb units 0,1,2 β€” 6 leaked device structs in 2 cycles, unbounded by repetition. Also visible: iicbus0's softc address changes between STATs (freed on detach, reallocated on re-attach) while the children persist β€” the asymmetry that makes the leak.
  • With iic.ko attachable (iicbus.ko + DF-5559 fix), run_b_5559.log: /dev/iic0 β†’ /dev/iic0,1 β†’ /dev/iic0,1,2,3 β€” duplicate char devices bound to ONE physical iicbus0, each with independent sc_addr state; also further breaks iicbus_transfer_gen's nkid != 1 check (iiconf.c:365).

Reachability honesty

The cycle requires a detach+re-attach of an iicbus whose bridge stays resident. In-tree stock configs are partly fenced by MODULE_DEPEND(iicbb, iicbus) (kldunload iicbus refused while iicbb.ko loaded), so the practical triggers are: a kernel-compiled bridge (device iicbb in the kernel config) with modular iicbus, an out-of-tree bridge without the dependency, or any rescan/driver-rebind flow. The rig drives the identical production functions, so the code path is proven; the privilege gate is root (kldload/rescan). Severity Low per family rubric.

Fix validation

fix.diff guards each trio member with device_find_child(dev, name, -1) == NULL. Fixed module (run_d_fixed.log, run_d_fixed_dmesg.txt): after creating two extra children (df5632c via BUS_ADD_CHILD, df5632k0 via device_add_child) and running 2 cycles, nchildren=5 before AND after β€” no growth; unload clean; guest healthy.

Distinction from the family findings

DF-5521/DF-5563 are iicsmb/iiconf teardown protocol bugs; DF-5559 is the ivar consumer. This finding is the attach-side topology bug in iicbus.c itself (its only prior mention was a 2026-10-06 task_log lead from the pcf pass, now root-caused and proven).

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

fixed
baseline reproduced→ patch + rebuild →patched clean

With iicbus.ko rebuilt from fix.diff, 2 detach/re-attach cycles leave nchildren constant (5 incl. two rig-added children; the trio does not duplicate) and unload is clean; baseline growth 3->9 eliminated.

run_d_fixed_dmesg.txt ('CYCLE-done: nchildren=5' before and after cycles; 'RUN-D-DONE'; no trap)
↓ fix.diffper-fix-DF-5633

Confirmed kernel references

Detail

Exploit chain

root: detach+re-attach iicbus0 (kldunload/kldload iicbus.ko with a kernel-resident bridge, out-of-tree bridge without MODULE_DEPEND, or rescan/rebind) -> each cycle: device_detach(iicbus0) leaves children alive -> device_probe_and_attach -> iicbus_attach mints a fresh trio -> unbounded M_BUS leak + duplicate iicN/iicsmbN units -> duplicate /dev/iicN nodes with divergent sc_addr on one wire.

Evidence (decisive lines)

run_a2_dmesg.txt (stock: 'STAT: nchildren=3' -> 'CYCLE-done: nchildren=9' after 2 production device_detach/device_probe_and_attach cycles, ic/iic/iicsmb units 0,1,2; softc address changes between STATs proving detach freed it while children persisted); run_b_5559.log ('/dev/iic0' -> '/dev/iic0 /dev/iic1' -> '/dev/iic0..3' on one iicbus0); run_d_fixed_dmesg.txt (fixed: 'CYCLE-done: nchildren=5' stable across cycles)

PoC changes

Rig lives in ../DF-5632/ (df5632_rig.c): /dev/df5632 ioctl CYCLE drives production device_detach()+device_probe_and_attach() on iicbus0; duplicate-node demonstration uses iicbus.ko rebuilt with DF-5559's validated fix so production iic.ko can attach.

Verified recommended fix

Guard the trio in iicbus_attach with device_find_child(dev, "ic"/"iic"/"iicsmb", -1) == NULL before each creation (mirroring iic_identify's own guard), routing through the fixed bus_add_child so ivars are allocated (fix.diff).

Verdict

REPRODUCED on stock iicbus.ko: iicbus_attach (iicbus.c:144-146) re-creates the ic/iic/iicsmb trio unguarded on every attach, while neither iicbus_detach nor devclass_delete_driver ever deletes the FIXEDCLASS children -- each detach+re-attach cycle of an iicbus with a resident bridge leaks 3 struct bsd_device (M_BUS) and mints duplicate devclass units; with iic.ko attachable this produces duplicate /dev/iicN character devices on ONE physical bus (independent sc_addr state, further breaking iicbus_transfer_gen's nkid!=1 check). 2 cycles: nchildren 3->9; /dev/iic0 -> /dev/iic0..3. fix.diff guards the trio with device_find_child (as iic_identify already does); fixed module: nchildren constant across cycles.