# DF-5595 — sim refcount release protocol is non-atomic and unenforced (asymmetric with its acquisition side)

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

## What

* Acquisition: `xpt_bus_register()` takes a sim reference with
  `atomic_add_int(&sim->refcount, 1)` (cam_xpt.c:4322) under the sim lock +
  topo lock.
* Release: `cam_sim_release()` (cam_sim.c:246-255) does a plain
  `if (sim->refcount == 1) { ... kfree ... } else { --sim->refcount; }` —
  a non-atomic read-compare-decrement/free with **no lock requirement
  checked or documented** anywhere in cam_sim.c.
* Every sibling finalizer in the same subsystem uses atomic cmpset loops
  precisely because these objects die concurrently:
  `xpt_release_bus` (cam_xpt.c:4979-5008), `xpt_release_target`
  (5048-5070), `xpt_release_device` (5192-5232). `cam_sim_release()` is the
  lone pre-modernization holdout (and DF-5552 documented the same defect
  pattern one level down, on the devq).

Two unlocked/unserialized `cam_sim_release()` calls racing (or a release
racing an acquisition outside the sim lock) lose updates: both read the same
count, both write count-1 → reference leak (sim never freed, devq never
released), or both read 1 → double `kfree(sim)` + double
`cam_simq_release(devq)`. In-tree call sites (`xpt_bus_deregister` at
cam_xpt.c:4484, driver `cam_sim_free`) are serialized only by per-driver lock
convention — nothing in cam_sim.c enforces or documents it.

## Status

Hardening-class: no in-tree unsynchronized release pair demonstrated (same
standing as DF-5552, its devq sibling). The fix is subsumed by DF-5592's
validated `fix.diff`, which rewrites `cam_sim_release()` into the atomic
cmpset protocol mirroring `xpt_release_bus()` — one more reason that fix is
the right shape. Filed Low / likely; PoC untested.
