DF-5632 / verdict.json
{ "finding_id": "DF-5632", "status": "reproduced", "verdict": "REPRODUCED on stock iicbus.ko via two independent triggers (module-load of an identify-pattern client driver, and a direct BUS_ADD_CHILD ioctl): the 4-arg/int iicbus_add_child registered against DragonFlys 5-arg/device_t bus_add_child interface misreads every dispatched argument (order<-parent pointer, name<-order int, unit<-name string address) and returns NULL to callers expecting the new child; the classless negative-unit child it creates reaches devclass_add_device, whose dc->devices[dev->unit]=dev is an out-of-bounds 8-byte kernel write below the devclass device array (Fatal trap 12, supervisor write, devclass_add_device+0xf6, fault VA 0xfffffffc13...). With fix.diff (correct 5-arg prototype returning the child + ivar allocation) BUS_ADD_CHILD returns a real device_t and creates the rendezvous correctly; no panic anywhere.", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "root: kldload an iicbus client driver whose identify follows the documented device_if.m pattern (iic.c:104-110 verbatim) -> BUS_ADD_CHILD(iicbusN,...) dispatch into skewed iicbus_add_child -> ghost child with negative wired unit -> first probe -> devclass_alloc_unit passes negative unit through -> devclass_add_device writes device_t OOB below dc->devices -> kernel memory corruption / Fatal trap 12. Trigger is root-gated (kldload); in-tree iic/iicsmb are masked only by the pre-created trio.", "evidence": "panic_skew_stock.txt (trigger 1: kldload, fault VA 0xfffffffc131cb558, Stopped at devclass_add_device+0xf6); panic_skew_addchild.txt (trigger 2: BUS_ADD_CHILD ioctl on a verified-healthy bus, fault VA 0xfffffffc131cc228, same PC); run_d_fixed.log + run_d_fixed_dmesg.txt (fixed: BUS_ADD_CHILD returned 0xfffff801175bc9c0, child devclass=df5632c unit=0, no panic); df5632_rig.c (rig: fakebb0->iicbb0->iicbus0 production chain + df5632k identify client)", "kernel_refs": [ "sys/bus/iicbus/iicbus.c:57", "sys/bus/iicbus/iicbus.c:66", "sys/bus/iicbus/iicbus.c:164-171", "sys/kern/bus_if.m:128-134", "sys/kern/subr_bus.c:devclass_add_device", "sys/kern/subr_bus.c:devclass_alloc_unit", "sys/bus/iicbus/iic.c:104-110", "sys/bus/iicbus/iicsmb.c:137-142", "sys/platform/pc64/x86_64/nexus.c:90,293" ], "poc_changes": "Rig adapted from the DF-5563 fakebb harness (same GPIO emulator): added the df5632k identify-based client driver (verbatim iic_identify pattern), an hw.df5632.identify_add gate so non-skew tests can load cleanly on stock, and /dev/df5632 ioctls driving production newbus APIs (BUS_ADD_CHILD, device_detach, device_probe_and_attach, BUS_READ/WRITE_IVAR, iicbus_request_bus).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 1500, "guest_dirty": 0, "build_cmd": "sh /root/df5632/build.sh (guest, as root): builds iicbb_reg.ko + df5632_rig.ko (make SYSDIR=/usr/src/sys), dfctl (cc), mod_5559/s/iicbus/iicbus.ko (stock+DF-5559 fix) and mod_fixed/s/iicbus/iicbus.ko (stock+fix.diff)", "run_cmd": "sh /root/df5632/run_a_stock.sh (stock baseline); skew triggers: kldload of the rig (panic_skew_stock.txt) and ./dfctl addchild (panic_skew_addchild.txt); fixed: sh /root/df5632/run_d_fixed.sh", "code_hash": "df5632_rig.c 89e5ff9630fa73906f5e0fb4b882e484dcc0014050cbfc65bc61801f8796bc03; dfctl.c 3df0b2325c149ced7c34f06d76e9878f7a86b7202fa98ce214ad261f98821210; fix.diff 5c6a4f06273c660085838cf1246404aaf04a458d0e809068eb49e45301a77690", "notes": "In-tree iic.ko/iicsmb.ko escape the skew only because iicbus_attach pre-creates their rendezvous children (iicbus.c:144-146) so the identify guards never fire -- an accident, not a protection. The OOB write index is the low 32 bits of a kernel rodata address (fixed per build, not attacker-chosen); the written value is a device_t heap pointer. Trigger is kldload (root), hence Medium per family rubric (DF-5521/DF-5563 band, one notch up because the standard driver-authoring pattern self-destructs at load).", "recommended_fix": "Re-type iicbus_add_child to the DFly bus_add_child contract: static device_t iicbus_add_child(device_t bus, device_t parent, int order, const char *name, int unit) { child = device_add_child_ordered(parent, order, name, unit); allocate struct iicbus_ivar; device_set_ivars(child, ivar); return child; } -- and drop the redundant bus_generic_attach() call (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "With only iicbus.ko rebuilt from fix.diff, BUS_ADD_CHILD returns a real device_t (0xfffff801175bc9c0) and creates the requested rendezvous child (devclass=df5632c unit=0); the previous Fatal-trap-12 OOB write at devclass_add_device+0xf2/0xf6 is gone under both triggers equivalents; load, addchild, 2 detach/reattach cycles and unload all clean, guest healthy.", "fix_evidence": "run_d_fixed.log (dfctl addchild: kernel rc=0 v0=0xfffff801175bc9c0), run_d_fixed_dmesg.txt (child[3] devclass=df5632c unit=0, RUN-D-DONE, no trap)" } |