smbus_attach unconditionally re-creates the generic child and the whole 96-address probe child set on every attach: children survive every teardown path, so each detach+re-attach cycle leaks the full set of struct bsd_device and mints duplicate remapped /dev/smbN[-XX] nodes whose forced-slave address encoding no longer matches the device it probed
Summary
No first-attach guard and no deletion counterpart; children survive kldunload (detaches-only) and the viapm_586b asymmetric bridge teardown, so every re-attach mints a SECOND full set while devclass remaps the duplicates' wired units. Reproduced on the production-code rig: nchildren 4->44 after 10 cycles (+4/cycle), /dev/smb* nodes 4->44; the re-minted probe children land on remapped units whose ioctl path forces a slave address nothing answers, while duplicate plain nodes pile onto one physical bus. Root-gated unbounded M_BUS leak (~0x2c0 x up to 97 per cycle) + topology confusion for cyapa/atmel/ipmi. DF-5633 twin; first-attach-guard fix validated (constant across 9 cycles).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5642 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df5642_rig.c | β | 13.9 KB | view raw | |
| df5642ctl.c | β | 1.5 KB | view raw | |
| Makefile | β | 267 B | β download | |
| build.log | β | 1.2 KB | view raw | |
| run_a.sh | β | 1.6 KB | view raw | |
| run_a.log | β | 1.3 KB | view raw | |
| run_a2.log | β | 896 B | view raw | |
| run_a_fixed.log | β | 190 B | view raw | |
| README.md | β | 3.6 KB | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| verdict.json | β | 5.1 KB | view raw | |
| fix.diff | β | 1.5 KB | view raw | |
| env.txt | β | 1.1 KB | view raw |
DF-5642 β smbus_attach unconditionally re-creates the whole child set on every attach β unbounded child leak + duplicate /dev/smbN[-XX] nodes on one bus
- File under audit:
sys/bus/smbus/smbus.c(pass 2) - Class: resource leak / device-topology corruption (DF-5633 twin at the smbus layer)
- Severity: Low (root-gated: kldunload/kldload of a bridge with surviving children, rescan/rebind)
- Verdict: REPRODUCED (4β44 children after 10 cycles, +4/cycle;
/dev/smb*4β44; duplicate forced-slave nodes with corrupted address encoding), fix validated (children constant across cycles on the patched kernel)
The defect
smbus_attach (smbus.c:94-97) populates the bus unconditionally:
device_add_child(dev, NULL, device_get_unit(dev)); /* generic smb child */
for (addr = 16; addr < 112; ++addr)
smbus_probe_device(dev, addr); /* per-address smb children */
with no re-creation guard, while smbus_detach (smbus.c:105-115) only
runs bus_generic_detach and never deletes the children. Children survive
every teardown path:
devclass_delete_driver(the kldunload path,subr_bus.c:888-897) detaches devices but never deletes them;- the DF-5633-class asymmetric bridge detach:
viapm_586b_detach(viapm.c:423-430) detaches the shared-attachsmbuschild (viapm.c:362) but only deletesiicbb; - any rescan/rebind that detaches and re-attaches the smbus device.
Every later device_probe_and_attach(smbus0) (exactly what
bus_generic_driver_added, subr_bus.c:3012-3015, does on driver
re-registration) re-runs smbus_attach and mints a second full child
set:
- +1
struct bsd_device(generic child) + one per responding address (probe loop) per cycle, unbounded (M_BUS leak, ~0x2c0 bytes each); - the new children take remapped units (
devclass_alloc_unit,subr_bus.c:1072-1081), so each cycle mints duplicate/dev/smbN[-XX]character devices on ONE physical bus β e.g. the second copy of the address-0x10 probe child lands at unit 1041 and is exposed as/dev/smb0-11, whose ioctl path (smb.c:229-231) then forces slave address 0x11 β an address nothing responds on: the node-name β physical-address contract (smb.c:134-141,cyapa.c:466-472) is corrupted.
Reproduction (guest, stock kernel + production-code KLD rig)
Family-standard rig (df5642_rig.c): a fake SMBus controller on nexus
registers the real in-kernel smbus driver (DRIVER_MODULE(smbus,
fakesmbb, smbus_driver, ...), mirroring intpm.c:903) and creates
smbus0 exactly like intpm.c:331-337. The emulated controller answers
addresses 0x10/0x50/0x67, so each attach mints 1 generic + 3 probe
children, claimed by the real in-kernel smb driver β real
make_dev nodes.
run_a.sh / run_a2.log β production APIs only
(device_detach(smbus0) + device_probe_and_attach(smbus0)):
[df5642] attach: nchildren=4 (smb0 + smb1040/1104/1127) node_count= 4 (/dev/smb0 smb0-10 smb0-50 smb0-67) [df5642] after-cycle: nchildren=8 (+4 per cycle, deterministic) [df5642] after-cycle: nchildren=44 (10 cycles) FINAL_node_count= 44 /dev/smb0 ... smb0-11 smb0-12 smb0-13 ... smb1 smb2 smb3 ... (duplicates)
Each cycle's new probe children land on remapped units (1041..) β
duplicate smb0-XX nodes whose -XX no longer names the address they
probed.
Fix (validated)
fix.diff populates the bus only when it has no children yet
(device_get_children guard in smbus_attach). On the patched kernel
(run_a_fixed.log): nchildren stays 4 across cycles, node count stays 4.
DF-5642 VERDICT β smbus_attach unguarded child re-creation (leak + duplicate /dev nodes)
Status: REPRODUCED, fix validated. Impact: dos (unbounded kernel heap leak + duplicate/corrupted /dev/smbN[-XX] topology). Root-gated trigger.
What was proven, on what code
The rig (df5642_rig.c, this pack) registers the REAL in-kernel smbus
driver (DRIVER_MODULE(smbus, fakesmbb, smbus_driver, smbus_devclass, ...)
β the same registration intpm.c:903 performs for the PIIX4) under a fake
controller on nexus, and creates smbus0 exactly like intpm.c:331-337
(device_add_child(dev, "smbus", -1) + device_probe_and_attach). The
production smbus_attach (smbus.c:94-97) then runs unmodified: it adds
the unnamed generic child and drives 96 smbus_trans probes against the
emulated controller (which answers 0x10/0x50/0x67), creating the
per-address children; the real smb driver claims them and smb_attach
(smb.c:139-141) mints real /dev/smbN[-XX] devfs nodes. Nothing in the
chain is stubbed except the wire.
/dev/df5642 ioctls call the production APIs device_detach(smbus0)
+ device_probe_and_attach(smbus0) β the same functions the module
handlers invoke on kldunload/kldload (devclass_delete_driver,
subr_bus.c:888-897, detaches but never deletes children;
bus_generic_driver_added, subr_bus.c:3012-3015, re-probes them).
Baseline (stock kernel, run_a.log + run_a2.log)
- initial: nchildren=4 (
smb0,smb1040,smb1104,smb1127) β/dev/smb0 smb0-10 smb0-50 smb0-67; - each cycle:
device_detach=0,device_probe_and_attach=0,after-cycle: nchildrengrows by exactly 4 (1 generic + 3 probe children) β 10 cycles: 4 β 44, node count 4 β 44; - the re-minted probe children take remapped units
(
devclass_alloc_unit,subr_bus.c:1072-1081): e.g. the second copy of the address-0x10 probe child lands at unit 1041 β/dev/smb0-11, andsmbioctlthen FORCES slave 0x11 on it (smb.c:229-231) β an address no device answers. The node-name β physical-address contract (smb.c:134-141,cyapa.c:466-472) is corrupted while duplicate plain nodes (smb1,smb2, ...) pile up on one physical bus.
Root cause is exactly smbus.c:94-97 (no first-attach guard) +
smbus.c:105-115 (detach never deletes children) + the family's
teardown asymmetry (viapm_586b_detach, viapm.c:423-430, deletes
iicbb but not the smbus child created at viapm.c:362).
Fix validation (fix.diff, run_a_fixed.log)
fix.diff populates the bus only when device_get_children reports it
empty. Patched kernel (rebuilt with nativekernel, boot #1 Fri Oct 9
04:55, smb.ko loaded first): 9 cycles β after-cycle: nchildren=5
every time (the 5th is the load-order identify child), /dev/smb*
count constant at 5. Baseline growth eliminated.
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 fix.diff (first-attach guard) built into the guest kernel, 9 detach/re-attach cycles keep nchildren=5 and /dev/smb* count constant at 5 (baseline: +4 per cycle, 4->44); unload/reload clean, no trap.
run_a_fixed.log ('after9: 5 nodes'; dmesg 9x 'after-cycle: nchildren=5'; final ls shows exactly smb0 smb1 smb0-10 smb0-50 smb0-67)
Confirmed kernel references
Detail
Exploit chain
root: detach+re-attach smbus0 (kldunload/kldload of a bridge whose smbus child survives -- viapm 586b, out-of-tree bridges, rescan/rebind, or devclass_delete_driver of the smbus driver with a resident parent) -> each cycle device_add_child mints 1+N struct bsd_device (M_BUS, ~0x2c0 bytes each) while the survivors re-attach -> unbounded heap leak + duplicate remapped /dev/smbN[-XX] nodes on one physical bus with corrupted slave-address binding (smb.c:229-231 forces slave=unit&1023).
Evidence (decisive lines)
run_a2.log ('attach: nchildren=4' -> 'after-cycle: nchildren=8..44', FINAL_node_count=44; duplicate node list smb0-11..13/51..53/68..6a + smb1..6); run_a.log (per-cycle device_detach=0/reattach=0 with nchildren growth); run_a_fixed.log (patched: 'after9: 5 nodes', 9x 'after-cycle: nchildren=5'); env.txt (stock vs patched uname); panic_raw.txt also holds the serial history.
PoC changes
Rig in this pack (df5642_rig.c): fake SMBus controller on nexus registers the real in-kernel smbus driver (DRIVER_MODULE(smbus, fakesmbb, ...) mirroring intpm.c:903) and creates smbus0 like intpm.c:331-337; /dev/df5642 ioctls STAT/CYCLE n drive the production device_detach/device_probe_and_attach APIs. Two build iterations fixed ioccom include / dev_ops prototypes / 64-bit respond mask; smb.ko is in-kernel on the stock guest kernel (the rebuilt fix-validation kernel needs kldload /boot/kernel/smb.ko first: repo config lacks 'device smb').
Verified recommended fix
Populate the bus only when it has no children yet (device_get_children guard in smbus_attach); optionally also device_delete_children in smbus_detach.
Verdict
REPRODUCED on the stock kernel via a production-code KLD rig hosting the real in-kernel smbus driver (registered on a fake controller exactly like intpm.c:903, smbus0 created exactly like intpm.c:331-337): smbus_attach (smbus.c:94-97) re-creates the generic child AND the whole address-probe child set on EVERY attach, while smbus_detach (smbus.c:105-115) and every teardown path (devclass_delete_driver subr_bus.c:888-897; viapm_586b_detach viapm.c:423-430 deletes iicbb but not the smbus child created at viapm.c:362) only detach children, never delete them. Each device_detach(smbus0)+device_probe_and_attach(smbus0) cycle (the production kldunload/kldload choreography) leaked the full set: nchildren 4->44 and /dev/smb node count 4->44 after 10 cycles (+4/cycle deterministic); re-minted probe children take remapped units (devclass_alloc_unit subr_bus.c:1072-1081) so duplicate forced-slave nodes appear with corrupted address encoding (second copy of address 0x10 lands at unit 1041 -> /dev/smb0-11, whose ioctl path forces slave 0x11 -- an address nothing answers). fix.diff guards population on first attach (device_get_children); on the patched kernel (nativekernel rebuild, boot #1 Oct 9) 9 cycles keep nchildren=5 and node count constant -- baseline growth eliminated.
No comments yet.