# PoC DF-4783 — mmcsd(4) teardown vs data path (DF-4646 family): NOT reproduced with faithful workloads; 3/3 panics with an over-strong kernel hammer; full analysis inside

## Rig

The guest boots with a real emulated SDHCI controller and card:

```
QEMU_EXTRA="-device sdhci-pci -drive id=sddrive,if=none,format=raw,file=sd.img -device sd-card,drive=sddrive"
```

`sdhci_pci_probe()` matches class 0x08/0x05 generically (sdhci_pci.c:300-305),
the card enumerates (`mmcsd0: 256MB <SD Memory Card> at mmc0 26MHz/4bit`),
and `/dev/mmcsd0` is live. `mmcrace.ko` then drives:

- `action=10` — hammer on: 16 kthreads issue real `BUF_CMD_READ` bios via
  `dev_dstrategy()` (the exact call the buffer cache makes), each bio using
  the buf's embedded `b_bio1` layer (required — the whole-disk cdev routes
  through `subr_diskslice dsstrategy()/push_bio()`).
- `action=1`  — the exact card-removal teardown path:
  `device_delete_child(mmc0, mmcsd0)` == `mmc_delete_cards()` (mmc.c:259)
  == `sdhci_cleanup_slot()` (sdhci.c:874) == card-detect removal ==
  `mmcsd_detach()` + newbus softc free.
- `action=2`  — re-insert: re-add the child with the saved ivars
  (device_add_child + device_probe_and_attach, i.e. `mmc_scan()` re-discovery).
- `mmhold.c` — *faithful* userland holder: opens /dev/mmcsd0 once and
  pread()s on the same fd forever (fd pins the cdev via specfs, exactly
  like a mounted filesystem or busy raw device).

## Results (all fresh boots)

1. **Faithful holder (mmhold) across teardown:** 10101 successful reads,
   then every pread returns `EBADF` (destroy_dev re-vectors ops to
   dead_dev_ops); process keeps running, guest healthy. Same result
   nvme's DF-4646 rig called "nvzombie".
2. **Faithful battery: 8 mmhold processes × 10 delete/re-insert cycles:**
   ALL cycles completed, guest up, no panic (`userbat.log`).
3. **Kernel hammer (dev_dstrategy without a cdev reference) × cycling:**
   **panic 3/3 fresh boots**, all inside the teardown window:
   - runs 1-2: `Stopped at dev_dstrategy+0x1b: movl 0xc(%rax),%r12d`
     (dev_need_mplock dereferencing the freed cdev)
   - run 3: `Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi`
     (MMCSD_LOCK on freed memory)

   Interpretation (verified against dev_dopen semantics): `dev_dopen()`
   does NOT raise the cdev sysref — the rig's "open" never pinned the cdev,
   so the hammer was racing a *freed cdev*, which no in-tree caller does
   (specfs/vn_strategy callers hold references). These panics demonstrate
   the missing serialization is real, but they over-state the primitive:
   they are NOT reachable by a legitimate workload, so the finding is
   classified **not_reproduced** for faithful triggers.

## The residual code-level race (why this is still a finding)

`mmcsd_detach()` (mmcsd.c:209-248) — unlike the data path — has **no
exclusion** mechanism:

- `mmcsd_strategy()` reads `si_drv1` (mmcsd.c:310) and takes `sc->sc_lock`
  (mmcsd.c:311) with nothing preventing `lockuninit(&sc->sc_lock)`
  (mmcsd.c:245) or the newbus softc free from racing a caller parked
  between those two instructions, or parked on the lock when the free
  lands. `si_drv1` is never cleared (nothing NULLs it), so any strategy
  entry that resolves the pointer before the free walks freed memory
  afterwards (run-3 signature).
- `mmcsd_open()` (mmcsd.c:286-295) has the same shape.
- The queue flush (mmcsd.c:233-239) runs *outside* the lock (provably safe
  today only because the enqueue-condition check and detach's
  running/suspend update are both under the lock).
- The `suspend > 0` arm of the enqueue condition (mmcsd.c:312) deliberately
  queues bios into a consumerless queue with no bound (see DF-4784).

The DF-4646 nvme twin crashed deterministically because its teardown freed
queues its *own still-running* admin thread/IRQs used; mmcsd's equivalent
(task thread) is properly joined, and devfs's ops re-vector blocks new
post-teardown work — so what remains here is a genuine but narrow
in-flight-stall TOCTOU (CWE-362/416), not a guaranteed UAF.

## Reproduce

```
# faithful (survives):
cd /root/mmcrig && make && kldload ./mmcrace.ko
nohup /root/mmcrig/mmhold > /root/mmhold.log 2>&1 &
sysctl hw.mmcrace.action=1      # card removal under load
# aggressive (panics 3/3):
sysctl hw.mmcrace.action=10; sleep 4
sysctl hw.mmcrace.action=1; sysctl hw.mmcrace.action=2   # cycle x N
```
