# DF-5635 — `iicbus_detach` swallows `bus_generic_detach()` failure → driverless NULL-softc bus with an attached child → NULL-page fault in `iicbus_request_bus`

* File under audit: `sys/bus/iicbus/iicbus.c` (pass 2)
* Class: lifecycle error-handling → kernel NULL-page out-of-bounds read →
  panic (local DoS)
* Severity: Low (requires a child whose detach fails — root-gated setup;
  in-tree iicsmb's DF-5521-guarded detach is the natural producer)
* Verdict: **REPRODUCED** (Fatal trap 12, fault VA 0x8, at
  `iicbus_request_bus+0x89: movq 0x8(%r12),%rax` = `sc->owner`,
  iiconf.c:101), fix validated

## The defect

```c
static int
iicbus_detach(device_t dev)                     /* iicbus.c:156-162 */
{
	iicbus_reset(dev, IIC_FASTEST, 0, NULL);
	bus_generic_detach(dev);                 /* return value DISCARDED */
	return (0);                              /* always success */
}
```

`bus_generic_detach` (subr_bus.c) detaches children and returns the first
child's error (e.g. EBUSY from a busy client). `iicbus_detach` ignores it
and returns 0, so `device_detach(iicbus0)` believes the driver detached
cleanly and completes the teardown:

* `device_set_driver(iicbus0, NULL)` — **softc kfree'd** (subr_bus.c,
  `device_set_driver`) and kobj switched to `null_class`;
* `dev->state = DS_NOTPRESENT`.

…while the failing child is still `DS_ATTACHED` under the now-driverless
bus. The child's very next bus operation,
`iicbus_request_bus(bus, dev, how)` (`iiconf.c:86-117`), does

```c
struct iicbus_softc *sc = device_get_softc(bus);   /* iiconf.c:88: NULL */
...
if (sc->owner && sc->owner != dev)                 /* iiconf.c:101: *0x8 */
```

→ read at address `offsetof(struct iicbus_softc, owner) == 8` on a NULL
base → **Fatal trap 12**.

The DF-5563 question ("does the iiconf drain/dying protocol need anything
from this file's detach?") is answered **yes**: DF-5563's validated fix
already patches `iicbus_detach` with the dying-flag + `wakeup(sc)` + drain
loop this file owes the waiter protocol — that half is DF-5563's and is
not re-reported here. This finding covers the *independent* defect living
at the same lines: the swallowed child-detach error.

## Reproduction (guest, stock modules)

`run_c_panic.sh` / `panic_5635_stock.txt` — the rig attaches a client
child (`df5632k0`) whose detach returns EBUSY on demand, then drives the
production APIs:

```
[df5632] df5632k0 detach: REFUSING (EBUSY)
iicbus0: detached
[df5632] DETACH_BUS: device_detach(iicbus0) returned 0  (child df5632k0 busy=1)
[df5632] DETACH_BUS-post: ... driver=(NONE-DRIVERLESS) state=0 softc=0
[df5632] DETACH_BUS-post: child[3] devclass=df5632k unit=0 driver=df5632k state=3
[df5632] REQBUS: calling iicbus_request_bus(iicbus0, self, WAIT|INTR)
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x8
Stopped at      iicbus_request_bus+0x89:  movq 0x8(%r12),%rax
```

The swallow (`returned 0` while the child refused), the inconsistent state
(driverless bus with softc=0 + attached child), and the predicted NULL-deref
(fault VA exactly `offsetof(owner)=8`) are all on the serial console.

## Fix (validated)

`fix.diff` propagates the error: `error = bus_generic_detach(dev);
return (error);` (and propagates `iicbus_reset`'s error too; composes
trivially with DF-5563's drain block). Fixed module
(`run_d_fixed.log`): `device_detach returned 16 (EBUSY)`, the bus stays
`driver=iicbus state=3 softc=0xfffff8008d581130` (attached, alive), the
child stays attached, and `iicbus_request_bus` on the still-attached bus
returns 0 (request+release clean). No panic anywhere.
