# DF-5643 — `smbus_detach` never checks `sc->owner`: the bus (and its softc) is torn down under the owning client and under parked requestes → NULL-deref panic + post-free write

* File under audit: `sys/bus/smbus/smbus.c` (pass 2)
* Class: lifetime/UAF (DF-5635 twin at the smbus layer; DF-5508
  "detach no-drain" twin)
* Severity: Low (root-gated detach: kldunload/rescan/hot-removal)
* Verdict: **REPRODUCED** (Fatal trap 12 in `smbus_release_bus+0x78`
  via the exact `smbioctl` exit path; plus a parked waiter woken on the
  FREED softc whose `smbus_request_bus` then *returned success* — a
  post-free read+write), fix validated (detach vetoes with EBUSY)

## The defect

`struct smbus_softc` carries `device_t owner` (`smbus.h:34`) — the
client exclusion state written by `smbus_request_bus` (`smbconf.c:134`).
`smbus_detach` (`smbus.c:105-115`) ignores it completely:

```c
static int
smbus_detach(device_t dev)
{
	int error;

	error = bus_generic_detach(dev);   /* no owner check, no drain */
	if (error)
		return (error);
	return (0);
}
```

A successful `device_detach(smbus0)` then:

1. **frees the softc**: `device_detach` → `device_set_driver(dev, NULL)`
   (`subr_bus.c:2150`) → `kfree(dev->softc)` (`subr_bus.c:1941-1946`);
2. under the **owner** (e.g. a `smbioctl` in flight on `/dev/smbN`,
   which holds the bus from `smb.c:240` to `smb.c:356`): its exit path
   `smbus_release_bus(bus, smbdev)` derefs
   `device_get_softc(bus) == NULL` at `smbconf.c:166` → page fault;
3. under **parked SMB_WAIT requesters** (tsleep'ing on `sc`,
   `smbconf.c:89/93`; in-tree: `ipmi_ssif.c`, `cyapa.c`): nothing wakes
   them (`smbus_detach` has no `wakeup`), they sleep on freed memory
   forever; any stray `wakeup(sc)` (e.g. `smbconf.c:177` broadcasts
   from another client on a reallocated softc) resumes them INTO the
   freed block: `sc->owner` read at `smbconf.c:128`, `sc->owner = dev`
   **write** at `smbconf.c:134`.

## Reproduction (guest, stock kernel + production-code KLD rig)

Same rig as DF-5642 (`/dev/df5642` ioctls drive the production APIs).

**RUN B** (`run_b.sh` / `run_b.log` + `panic.txt`): GRAB sets a
verifiable owner, then `device_detach(smbus0)`, then the owner's
release — the `smb.c:356` sequence:

```
[df5642] GRAB: request(fakesmbb0, DONTWAIT)=0 owner=0xfffff8011745d1e0
[df5642] VETO: pre-detach owner=0xfffff8011745d1e0 (must be non-NULL)
[df5642] VETO: device_detach(smbus0)=0 <<< BUS DETACHED UNDER ITS OWNER
[df5642] VETO: post-detach softc=0 (NULL = freed under the owner)
[df5642] RELFREE: calling smbus_release_bus(smbus0, fakesmbb0) with softc=0
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x0
Stopped at smbus_release_bus+0x78: cmpq %r13,(%r12)     <- sc->owner == dev, sc=NULL
```

**RUN C** (`run_c.sh` / `run_c.log`): parked waiter on the freed softc:

```
[df5642] GRAB: ... owner=0xfffff8011745d1e0
[df5642] PARK-T: calling request(nexus0, WAIT|NOINTR) softc=0xfffff8008d560cc8
[df5642] PARK: waiter started=1 done=0 (parked on softc=0xfffff8008d560cc8)
    -1 B5     smbreq   df5642_park            <- ps: parked, never woken by detach
[df5642] VETO: device_detach(smbus0)=0 <<< BUS DETACHED UNDER ITS OWNER
[df5642] VETO: post-detach softc=0 (NULL = freed under ... the parked waiter)
[df5642] WAKUAF: wakeup(freed sc=0xfffff8008d560cc8)
[df5642] PARK-T: request RETURNED 0 (post-free resume)   <- read+write on freed M_BUS
```

The waiter read the freed `sc->owner` (garbage), took the grant branch,
**wrote its device_t into the freed block** (`smbconf.c:134`), and
returned SUCCESS — a 8-byte pointer write into freed M_BUS memory
(whatever reallocated that size class is now corrupted; with
`smbus_release_bus`'s unconditional `wakeup(sc)` broadcast at
`smbconf.c:177`, the stray wake is deliverable by any other client's
release on the reallocated softc).

## Fix (validated)

`fix.diff` (shared with DF-5642's pack) makes `smbus_detach` refuse
with `EBUSY` while a client owns the bus. On the patched kernel
(`run_b_fixed.log`): `device_detach=16 (EBUSY)`, softc intact, the
owner's release returns 0 — no trap.
