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)
PoC verification
Evidence pack
findings/poc/DF-5633 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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..3on 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 byiic.ko, producing multiple/dev/iicNcharacter devices on ONE physical bus, each with its ownsc_addrstate β 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).
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=9with 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.koattachable (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 independentsc_addrstate; also further breaksiicbus_transfer_gen'snkid != 1check (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
fixedWith 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)
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.
No comments yet.