# DF-4227 — cyapa: detach/module-unload leaves open fds, blocked readers and kqueue hooks on a vanished softc → NULL-deref panic / dispatch through freed module ops

## Where
`sys/dev/smbus/cyapa/cyapa.c`

- `cyapa_detach()` — cyapa.c:545-571 — never checks `sc->count` (open fds),
  never wakes readers blocked in `cyaparead` (cyapa.c:749 `lksleep`), never
  `destroy_dev()`s the cdev made by `make_dev()` (cyapa.c:505-511); it only
  calls `dev_ops_remove_minor()` (cyapa.c:567).
- `cyaparead()` — cyapa.c:616 fetches `sc = CYAPA_SOFTC(minor(dev))` and
  cyapa.c:626 calls `cyapa_lock(sc)` — **no NULL check** (open/close/ioctl
  have one; read/write/kqfilter do not).
- `cyapawrite()` — cyapa.c:789 → cyapa.c:801 same missing check.
- `cyapakqfilter()` — cyapa.c:1069 → cyapa.c:1083 same missing check.

## Why it faults (source-certain chain)
1. Root opens `/dev/cyapa0-67` (0600 root:wheel — Xorg on a laptop does).
2. `kldunload cyapa` (module, not built into kernel — `sys/dev/smbus/cyapa/Makefile` KMOD)
   → `driver_module_handler` MOD_UNLOAD (subr_bus.c:3648-3655)
   → `devclass_delete_driver` → `device_detach` → `cyapa_detach`
   → `devclass_delete_device` sets `dc->devices[unit] = NULL`
   (sys/kern/subr_bus.c:1164).
3. The open fd still holds a cdev reference: `devfs_destroy_dev_by_ops`
   (devfs_core.c:863, worker at 1546) only unlinks the devfs node and drops
   refs — it does NOT drain open file descriptors.
4. Next `read()`/`write()`/`kqueue` on the open fd:
   `dev_dwrite/dev_dread` dispatches through `dev->si_ops` (`&cyapa_ops`,
   a static struct inside the module — freed after unload) into
   `cyaparead`, which does `CYAPA_SOFTC(minor)` =
   `devclass_get_softc(cyapa_devclass, unit)` → **NULL** (subr_bus.c:932-951)
   → `cyapa_lock(NULL)` → `lockmgr(&sc->lk)` on a near-NULL address →
   **kernel panic**.  If dispatch itself happens after module memory is
   freed/reused, this is a call through freed memory instead.
5. A reader already blocked in `lksleep(&sc->blocked, &sc->lk, ...)` at
   cyapa.c:749 is never woken by detach and sleeps on a softc that dies
   with the driver.

## Trigger (real hardware)
```
# on a machine with a Cypress APA trackpad (SMBus addr 0x67):
./trigger_detach_uaf &          # opens /dev/cyapa0-67, blocks in read()
sleep 1 && kldunload cyapa.ko   # panic in the reader
```

## On this audit guest (QEMU)
The attack surface cannot exist: no SMBus controller, and cyapa children are
only created for *responding* SMBus addresses (bus/smbus/smbus.c:120-140,
unit = `(smbus_unit << 11) | 0x400 | addr`), so the driver never attaches and
`/dev/cyapa*` never appears. Verified live — see `run.log`
(module builds, loads, sysctls appear, no attach, unloads cleanly).

## Build / run (guest, as root)
```
cc -O2 -Wall -o trigger_detach_uaf trigger_detach_uaf.c
./trigger_detach_uaf
```
