# DF-4276 — cbb_detach() stops the slot event thread AFTER deleting its children (pccbb.c)

Finding (final scope after verification): sys/dev/pccard/pccbb/pccbb.c —
CardBus bridge slot event-thread teardown ordering defect.

`cbb_detach()` (pccbb.c:279-306) runs

```c
error = device_delete_children(brdev);          /* pccbb.c:285 */
...
sc->flags |= CBB_KTHREAD_DONE;                  /* pccbb.c:294 */
if (sc->flags & CBB_KTHREAD_RUNNING) {          /* pccbb.c:295 */
        wakeup(&sc->generic_cv);                /* pccbb.c:297 */
        tsleep(sc->event_thread, 0, "cbbun", 0);/* pccbb.c:298 */
}
```

while `cbb_event_thread()` is still alive: it re-evaluates the socket at
least once per second (hz-timeout re-check loop, pccbb.c:479-484) and, with a
card present/absent, calls

- `cbb_insert()` (pccbb.c:460) -> `CARD_ATTACH_CARD(sc->cbdev)` (pccbb.c:517)
  or `exca_insert(&sc->exca[0])` (pccbb.c:509), or
- `cbb_removal()` (pccbb.c:444) -> `CARD_DETACH_CARD(sc->cbdev)` /
  `exca_removal(&sc->exca[0])` (pccbb.c:536-539), plus
- `cbb_destroy_res(sc)` (pccbb.c:541) walking `sc->rl`.

`device_delete_children()` frees the `cardbus`/`pccard` child devices
(`sc->cbdev`, `sc->exca[0].pccarddev` — neither pointer is cleared), and
child detach is slow by design (`cbb_pcic_power_disable_socket` sleeps
10+300 ms, pccbb.c:1246/1253; `cbb_cardbus_reset` 2x20-400 ms,
pccbb.c:914-925), so the overlap window during `kldunload -f cbb` /
bridge hot-removal is seconds wide. The thread operating on freed children
(use of dangling `device_t`, freed child softc via CARD_*/exca_* methods) and
re-attaching card children under a detaching bridge is a use-after-free /
dangling-reference race. Additionally, `CBB_KTHREAD_RUNNING` is set by the
thread itself at pccbb.c:431, so a detach racing thread startup (created at
attach, pccbb_pci.c:405, but not yet running) skips the tsleep entirely and
the softc is freed under a live thread.

Trigger: privileged detach (`kldunload -f cbb`) with a card inserted, or
physical bridge hot-removal — Low severity per the rubric (requires
privilege/hardware), but the window is wide and the outcome is UAF.

## What verification proved and disproved

Two candidate hang axes of this teardown were INVESTIGATED ON THE GUEST and
DISPROVEN (this is a feature of this pack — the negative result rules them
out for the whole file):

1. "Lost final wakeup => permanent 'cbbun' hang": DISPROVEN.
   `lwkt_switch_return()` unconditionally calls `wakeup(otd)` on the
   exiting thread's ident (sys/kern/lwkt_thread.c:884-899), so
   `tsleep(sc->event_thread, ...)` returns when the thread exits even if
   the explicit `wakeup(sc->event_thread)` (pccbb.c:487) was lost in the
   wakeup(generic_cv)->tsleep window. The replica module
   (df4276_rep.c) demonstrates this rescue on the guest.
2. "Non-atomic flags RMW loses CBB_KTHREAD_DONE": impossible by program
   order — the thread only reaches `flags &= ~CBB_KTHREAD_RUNNING`
   (pccbb.c:486) after observing DONE in the loop condition, and the
   detacher stores DONE exactly once before that.

The surviving defects are the ordering (children deleted before the thread
is stopped) and the RUNNING-flag startup window, which are source-certain
but require CardBus hardware + detach timing, absent from the QEMU guest.

## Reproduction (negative-result replica)

    ./build.sh     # df4276rep.ko
    ./run.sh       # kldload; the replica forces the lost-wakeup interleaving
                   # and shows the detacher STILL returns (thread-exit rescue)

Expected output (run.log):

    df4276: replica armed
    df4276: event thread tail complete; final wakeup() delivered into the void
    df4276: detach COMPLETED -- no lost wakeup
    df4276: RESULT: no lost wakeup

## Artifacts

- df4276_rep.c   — replica module source (driver-exact sleep/wakeup discipline)
- build.log, run.log, env.txt
- fix.diff       — reorder cbb_detach: stop the event thread FIRST
- verdict.json, manifest.json
