# DF-5592 — use-after-free: cam_sim freed while still pending in the camisr completion softint

`sys/bus/cam/cam_sim.c` pass-2 (GLM 5.3) — `cam_sim_release()` / `cam_sim_free()`.

## What

`cam_sim_release()` (cam_sim.c:241-256) performs the final `kfree(sim)` with **no
synchronization against the CAM deferred-completion subsystem**:

* `xpt_done()` (cam_xpt.c:4891-4926) is *lock-free by design* — it inserts the
  completed ccb on `sim->sim_doneq` under `sim->sim_spin` only, then links the
  sim onto the global `cam_simq` under `cam_simq_spin` and calls
  `setsoftcambio()`. This is the contract controller interrupt handlers rely on.
* `swi_cambio → camisr()` (cam_xpt.c:7389-7407) pops the sim off `cam_simq`
  under `cam_simq_spin` and only **then** takes `CAM_SIM_LOCK(sim)` — so a
  detach thread that already holds the sim lock runs its entire
  `xpt_bus_deregister()` + `cam_sim_free()` while camisr is blocked holding
  nothing but a raw `struct cam_sim *`.
* The standard driver detach protocol is exactly that: lock;
  `xpt_bus_deregister()`; `cam_sim_free()`; unlock —
  ahci_cam.c:210-227, virtio_scsi.c:675-687, atapi-cam.c:895-900,
  mpr_sas.c:955-956.

Result: `kfree(sim)` lands while the sim is still queued for / undergoing
completion processing. When the lock is released, `camisr` **writes** the freed
chunk (`sim->flags &= ~CAM_SIM_ON_DONEQ` at cam_xpt.c:7403 — note: plain
read-modify-write on freed memory), runs `lockmgr()` on the freed embedded
lock (7402), `spin_lock(&sim->sim_spin)` + walks `sim->sim_doneq` (7415-7417),
and dispatches `(*ccb_h->cbfcnp)(path->periph, ccb)` (7506) — a kernel
function-pointer call driven by freed-object contents.

## Reproduce (guest)

```
sh build.sh                      # builds ptsim3/ptsim3.ko (mini-SIM rig)
kldload ./ptsim3/ptsim3.ko
sysctl debug.ptsim3.go=1         # mode 1: silent UAF proof (marker callback)
sysctl debug.ptsim3.go=2         # mode 2: poison -> Fatal stop in camisr_runqueue
sysctl debug.ptsim3.go=4         # mode 4: fix-validation trail
```

The rig (ptsim3.c) is a contract-clean mini-SIM chassis (the DF-5550 ptsim2
design): it registers a real bus with production symbols, parks probe ccbs as
"hardware", releases exactly one via the REAL `xpt_done()` from a kthread that
holds no sim lock (the interrupt-handler privilege), lets the swi block on the
sim lock the rig holds, then executes the ahci_cam_detach() sequence
verbatim. Expected:

* mode 1 (baseline kernel): `[PTSIM3] *** UAF CONFIRMED: kernel ran a
  completion callback out of a cam_sim chunk that cam_sim_free() had already
  kfree()'d ***` — `marker_hit=1`.
* mode 2 (baseline kernel): `[Stopped at camisr_runqueue+0x53: movq
  0x20(%r15),%rax]` — DDB stop / Fatal trap 12 from the poisoned freed chunk.
* mode 4 on the FIXED kernel: `cam_sim_free() returned; chunk refcount now
  reads 1` (pin held ⇒ not freed), marker callback fires with the sim valid,
  chunk freed only after processing.

## Fix

`fix.diff` — pin the sim (atomic ref) while it is linked on `cam_simq`; camisr
drops the pin after processing; `cam_sim_release()` becomes an atomic cmpset
protocol mirroring `xpt_release_bus()`; final 1→0 transition under
`cam_simq_spin` (`xpt_sim_finalize`); `cam_dead_sim.refcount` pinned high.
Validated by rebuild + re-run (see VERDICT.md).
