# DF-5539 — iicbb_print_child() discloses an uninitialized kernel stack byte

`sys/bus/iicbus/iicbb.c:164-186`

## What

`iicbb_print_child()` declares `u_char oldaddr;` (iicbb.c:169) **uninitialized**,
hands `&oldaddr` to the parent driver's `iicbb_reset` method (iicbb.c:173), and —
whenever that call does *not* return `IIC_ENOADDR` — prints the byte to the
console and writes it back as the "restored" bus address (iicbb.c:179-182):

    iicbus0: <Philips I2C bus> on iicbb0 addr 0xff

No in-tree iicbb parent ever fulfills the "return 0 and fill `*oldaddr`"
contract: `lpbb_reset` returns `IIC_ENOADDR` on success and **returns 0 without
writing `*oldaddr` on its ppbus-busy path** (`sys/dev/misc/lpbb/lpbb.c:186-190`);
`viabb_reset` and `cxm_iic_reset` always return `IIC_ENOADDR`. So the only way
the else-branch (iicbb.c:177-183) can execute in-tree is with `oldaddr` never
written — every execution of those lines reads uninitialized kernel stack.

## How to reproduce (production-code KLD harness — no bit-bang hardware)

The guest has no GPIO/ppbus bit-bang hardware, so the harness instantiates the
**real, unmodified in-tree `iicbb.ko`** under a fake GPIO parent:

* `iicbb_reg.ko` — registers the real `iicbb_driver`/`iicbb_devclass` on a fake
  bus `fakebb`, exactly like the in-tree `DRIVER_MODULE(iicbb, lpbb/viapm/...)`
  declarations (iicbb.c:441-445).
* `iicbb_harness.ko` — a `fakebb` device on `nexus0` whose `iicbb_if` methods
  emulate the GPIO pins plus a wired I2C slave (START/STOP detection, SCL-edge
  sampling, ACK/NACK injection, TX pattern generator). `fakebb_reset()`
  faithfully reproduces lpbb's ppbus-busy contract violation: returns 0,
  never writes `*oldaddr`.

On load, newbus walks identify → probe → attach (`sys/kern/subr_bus.c:3007-3013`,
print at `subr_bus.c:2004`), the cascade fakebb0 → iicbb0 → iicbus0 runs inside
`fakebb_attach`, and `iicbb_print_child` prints the uninitialized byte. The
harness then drives the real transfer state machine through the public iicbus
API (write-echo, read-pattern, NACK → `IIC_ENOACK` + bus-stopped,
repeated-start read) to demonstrate the driver is exercised end-to-end.

### Build (on the DragonFly guest, as root, in /root/df5539)

    ./build.sh          # builds iicbb_reg.ko + 4 harness variants
                        # (a5/5a/37: stack pre-sprayed; none: no spray)

### Run

    kldload /boot/kernel/iicbus.ko
    kldload /boot/kernel/iicbb.ko
    kldload /usr/obj/root/df5539/build_reg/iicbb_reg.ko
    kldload /usr/obj/root/df5539/build_a5/iicbb_harness.ko   # etc. per variant
    dmesg | grep 'on iicbb0 addr'
    ./run.sh            # does all of the above + 8 load/unload cycles

### Expected

* Stock kernel/modules: `iicbus0: <Philips I2C bus> on iicbb0 addr 0xff`
  on **every** attach (16/16 observed; value is stack junk — allocator fill on
  this system, see VERDICT.md for the spray experiment and the disassembly
  proof that the slot is never written).
* State machine green: `write ok=1 read_ok=1`, `nacked-write: error=2
  (IIC_ENOACK=2) bus_started_after=0`, `repstart-read: byte=0xc0`.
* With `fix.diff` applied to iicbb.c and only `iicbb.ko` rebuilt:
  `addr 0x0` (deterministic, no uninitialized data), state machine unchanged.

## Fix

See `fix.diff` (also inline in the finding): initialize `oldaddr = 0` and take
the else-branch only on `error == IIC_NOERR`.
