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

iicbus_get_addr() returns uninitialized kernel stack β€” iic_probe() is a stack-garbage coin flip and /dev/iicN is dead-on-arrival

Summary

__BUS_ACCESSOR discards BUS_READ_IVAR's failure and nothing in the iicbus family implements read_ivar or allocates child ivars: the dispatch lands on kobj_error_method (ENXIO without writing *result) and iic_probe's attach decision reads uninitialized stack - correcting the prior lead (no NULL-deref; the defect is the discarded-failure uninit return). Verified: BUS_READ_IVAR error=6 with v untouched at 0x77777777, /dev/iic0 never created across 240+ re-probes on stock (also explains why the pass-1 iic bugs never surfaced in the field). ivar-accessor fix validated (error=0 v=0x0, attaches first kldload).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5559 Β· 7 files
FileTypeDescriptionSize
groom5558.c β€” 9.2 KB view raw
Makefile.groom β€” 184 B ↓ download
stock-probe-evidence.log β€” 4.4 KB view raw
dmesg.txt β€” 960 B view raw
env.txt β€” 541 B view raw
fix.diff β€” 2.1 KB view raw
verdict.json β€” 4.8 KB view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff applied to the guest's /usr/src copy of sys/bus/iicbus/iicbus.c; only iicbus.ko rebuilt (no kernel rebuild). Baseline stock: BUS_READ_IVAR ENXIO with result untouched; iic child never attaches (240+ re-probes ENXIO); /dev/iic0 absent. Patched: BUS_READ_IVAR error=0 with result=0 written; production iic.ko attaches on the first kldload; /dev/iic0 created and fully functional (DF-5558 PoC runs against it).

fix.diff; ../DF-5558/fix_build.log (iicbus.ko rebuild); dmesg.txt ('readivar: BUS_READ_IVAR error=0 ... v=0x0 (written)'); stock-probe-evidence.log (baseline failures).
↓ fix.diffper-fix-DF-5559

Confirmed kernel references

Detail

Evidence (decisive lines)

stock-probe-evidence.log (serial console: 240+ 'attachiic try N: device_probe_and_attach error=6' on stock + the readivar ENXIO/untouched line); dmesg.txt (fixed state: 'readivar: BUS_READ_IVAR error=0 v=0x0 (written)', iic0 attaches); ../DF-5558/run2-full.log ('iic0: <I2C generic I/O> on iicbus0' + /dev/iic0 with only iicbus.ko fixed); groom5558.c (proof tool: df5558.readivar / df5558.getaddr_spray / df5558.attachiic).

PoC changes

No seed PoC (pass-2 finding verified in the same run). Proof tool is groom5558.ko (sysctl df5558.readivar with a 0x77777777 sentinel; df5558.getaddr_spray two-function stack spray; df5558.attachiic controlled re-probe). The stack-pattern spray did not hit the accessor's exact slot (stable zeroed spill in the sysctl context) -- recorded honestly; the ENXIO/untouched dispatch proof plus the stock-vs-fixed probe behavior carry the result.

Verified recommended fix

Implement bus_read_ivar/bus_write_ivar on the iicbus driver returning a sane default (0) for IICBUS_IVAR_ADDR when no ivars exist (see fix.diff); longer term, allocate iicbus_ivar for children in bus_add_child as FreeBSD does.

Verdict

REPRODUCED (as an uninitialized-value use with deterministic stock effect). iic_probe() (sys/bus/iicbus/iic.c:115) gates /dev/iicN creation on iicbus_get_addr(), a __BUS_ACCESSOR-generated inline (iicbus.h:56 via sys/sys/bus.h:542-557) that discards BUS_READ_IVAR's error and returns its never-written local 'v'. No driver in the iicbus ancestry implements bus_read_ivar and ivars are never allocated (iicbus.c:59-73, 144-146, 164-171); the no-DEFAULT method resolution lands on kobj_error_method (makeobjops.awk:281-282, subr_kobj.c:69-72) which returns ENXIO without touching *result. Stock-kernel proof on the guest: (1) df5558.readivar -- BUS_READ_IVAR error=6 (ENXIO) with sentinel 0x77777777 UNTOUCHED; (2) with stock iicbus.ko+iic.ko the iic child exists but /dev/iic0 is never created -- 240+ device_probe_and_attach re-probes (incl. from zero-sprayed stacks) all ENXIO, because the probe context's kobj_init/kmalloc path rewrites the slot with garbage > 0 right before DEVICE_PROBE, while the sysctl context returns 0x00000000 -- the value is context-dependent stack residue; (3) the task_log lead is corrected: DragonFly's accessor does NOT deref device_get_ivars, so there is no NULL-deref panic -- the defect is the discarded-failure uninit return. Net stock effect: the production iic(4) interface is dead-on-arrival (also explaining why the pass-1 I2CRDWR findings were never seen in the wild). fix.diff (bus_read_ivar/bus_write_ivar on the iicbus driver, answering 0 for the unallocated addr ivar) validated by rebuilding only iicbus.ko: BUS_READ_IVAR error=0 v=0x0 (written), production iic.ko attaches on the first kldload, /dev/iic0 appears. Direct security impact is contained in-kernel (value is only compared); severity Low.