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

No struct iicbus_ivar is ever allocated for children (DF-5559's root-cause file): with DF-5559's accessor fix applied, iicbus_write_ivar returns success while silently discarding every address write

Summary

Children are created bare (ivars NULL), no child_detached exists: even WITH the DF-5559 accessor fix every write_ivar returns 0 while dropping the value (write 0x42 -> read 0x0 forever) - the official interface can never store a child address. Provider-side re-proof on stock (ENXIO, value untouched) plus the silent-drop demonstration on the DF-5559-fixed module; ivar-alloc + child_detached fix (bundled with DF-5632) round-trips 0x42. Also removes the dead DEVTOIICBUS macro casting to a nonexistent struct.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5634 Β· 8 files
FileTypeDescriptionSize
README.md β€” 3.0 KB ↓ raw
VERDICT.md β€” 1.4 KB ↓ raw
verdict.json β€” 3.3 KB view raw
manifest.json β€” 782 B view raw
fix.diff β€” 5.5 KB view raw
run_b_5559.log β€” 936 B view raw
build.log β€” 30.8 KB view raw
env.txt β€” 354 B view raw

DF-5634 β€” children are created with no struct iicbus_ivar ever allocated (the DF-5559 root): iicbus_write_ivar returns success while discarding every address write

  • File under audit: sys/bus/iicbus/iicbus.c (pass 2) β€” root-cause file of DF-5559, whose validated fix patched only the accessor half
  • Class: incomplete plumbing / silent data loss (hardening completion)
  • Severity: Info (no memory-safety consequence by itself; the uninitialized-read consequence was DF-5559)
  • Verdict: REPRODUCED (stock: ENXIO + untouched value; with DF-5559's fix: write returns 0 but the value is silently dropped), fix validated

The defect

struct iicbus_ivar (iicbus.h:44-47) exists and the generated accessor iicbus_get_addr() (iicbus.h:53-56, __BUS_ACCESSOR) routes through BUS_READ_IVAR/BUS_WRITE_IVAR, but nothing in iicbus.c ever:

  • allocates an ivar for a child β€” iicbus_attach (:144-146) and iicbus_add_child (:164-171) create children bare (make_device leaves dev->ivars = NULL, subr_bus.c);
  • implements bus_read_ivar/bus_write_ivar (absent from iicbus_methods, :59-73) β€” the uninitialized-stack half, filed and fixed as DF-5559;
  • frees ivars (bus_child_detached absent).

This is the "longer term allocate iicbus_ivar for children in bus_add_child per FreeBSD" item from the 2026-10-07 task_log lead. Even with DF-5559's validated fix (read/write_ivar added, NULL-tolerant), every child still has ivar == NULL, so:

case IICBUS_IVAR_ADDR:
        if (ivar != NULL)                 /* never true */
                ivar->addr = (uint32_t)value;
        return (0);                       /* success -- value discarded */

β€” iicbus_write_ivar reports success while dropping the write, so no code path can ever set an iicbus child address through the official interface (there is no in-tree writer today; the hazard is for the same future consumers DF-5559 flagged, and the accessor contract itself).

Reproduction (guest)

  • Stock (run_a2.log): BUS_WRITE_IVAR(iicbus0, child, IICBUS_IVAR_ADDR, 0x42) β†’ error 6 (ENXIO); BUS_READ_IVAR β†’ error 6 with the result variable untouched at 0x42424242 β€” nobody behind the method (DF-5559's uninit class, re-proven from the provider side).
  • iicbus.ko + DF-5559 fix (run_b_5559.log): WRITE_IVAR(0x42) error=0 (success!) but READ_IVAR val=0x0 β€” the write was silently discarded; iicbus_get_addr() == 0 forever.
  • Fixed (run_d_fixed.log): with fix.diff allocating ivars in the corrected bus_add_child (and freeing them in the new bus_child_detached): WRITE error=0, READ val=0x42, iicbus_get_addr=0x42 β€” the round trip works.

Fix

fix.diff (shared with DF-5632): iicbus_add_child allocates a zeroed struct iicbus_ivar and device_set_ivars(child, ivar); iicbus_child_detached frees it; the DF-5559 accessors now have a live ivar to serve. Also removes the dead DEVTOIICBUS macro (iicbus.c:44) that still casts to the nonexistent struct iicbus_device.

VERDICT.md
↓ download raw

DF-5634 VERDICT β€” REPRODUCED (silent ivar write drop; the DF-5559 root), fix validated

See README.md for the full narrative. Summary of the decisive evidence:

  • Stock (../DF-5632/run_a2.log): IVAR_RT: WRITE_IVAR(0x42) error=6, READ_IVAR error=6 val=0x42424242 β€” ENXIO, result untouched (the DF-5559 uninitialized class, seen from the provider side).
  • iicbus.ko + DF-5559's own validated fix (run_b_5559.log): WRITE_IVAR(0x42) error=0 then READ_IVAR error=0 val=0x0 iicbus_get_addr=0x0 β€” the write reports success and is silently discarded, because no child ever has an ivar allocated (iicbus.c:144-146, :164-171 create children bare; make_device leaves dev->ivars = NULL).
  • Fixed (../DF-5632/run_d_fixed.log): dfctl ivar: v1=0x42 v2=0x42 β€” write/read/accessor round trip works with fix.diff's allocation in the corrected bus_add_child + bus_child_detached free.

Impact: none by itself (hardening completion of the accessor contract); the memory-safety consequence of the missing read side was DF-5559.

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, the ivar round trip works on a production child: WRITE_IVAR(0x42) error=0, READ_IVAR val=0x42, iicbus_get_addr=0x42 -- the silent drop is gone and the accessor contract functions.

../DF-5632/run_d_fixed.log ('dfctl ivar: kernel rc=0 v0=0x0 v1=0x42 v2=0x42'); ../DF-5632/run_d_fixed_dmesg.txt ('[df5632] IVAR_RT: READ_IVAR error=0 val=0x42 iicbus_get_addr=0x42')
↓ fix.diffper-fix-DF-5634

Confirmed kernel references

Detail

Evidence (decisive lines)

run_b_5559.log + dmesg witness ('[df5632] IVAR_RT: WRITE_IVAR(0x42) error=0' then 'READ_IVAR error=0 val=0x0 iicbus_get_addr=0x0' -- silent drop with the DF-5559 fix loaded); stock witness in ../DF-5632/run_a2.log ('IVAR_RT: WRITE error=6', READ 'val=0x42424242' untouched); fixed witness in ../DF-5632/run_d_fixed.log ('dfctl ivar: v1=0x42 v2=0x42')

PoC changes

IVAR_RT ioctl in the ../DF-5632 rig drives production BUS_WRITE_IVAR/BUS_READ_IVAR/iicbus_get_addr on a real iicbus child; baseline uses iicbus.ko rebuilt with DF-5559's own validated fix.diff to isolate the silent-drop delta.

Verified recommended fix

Allocate a zeroed struct iicbus_ivar in iicbus_add_child (device_set_ivars) and free it in a bus_child_detached method; keep the DF-5559 read/write_ivar accessors (fix.diff).

Verdict

REPRODUCED: iicbus children are created with no struct iicbus_ivar ever allocated and no bus_child_detached to free one (iicbus.c:144-146,164-171 -- the DF-5559 root-cause file). Stock: BUS_WRITE/READ_IVAR -> ENXIO with the result variable untouched (the DF-5559 uninit class seen from the provider side). Even with DF-5559's validated accessor fix applied, every child still has ivar==NULL, so iicbus_write_ivar returns success while discarding the value (write 0x42 -> read 0x0, forever): the official interface can never store a child address. fix.diff allocates ivars in the corrected bus_add_child and frees them in a new bus_child_detached; fixed module round-trips 0x42.