# DF-4438 — mrsas_cam_attach error paths leave dangling `sc->sim_0`/`sc->sim_1`; caller unwind double-frees the `cam_sim`

**File:** `sys/dev/raid/mrsas/mrsas_cam.c`
**Severity:** Low · **CWE:** CWE-415 (Double Free) / CWE-459 (Incomplete Cleanup) · **Confidence:** certain (static call-sequence proof + on-guest logic mirror)

## What

`mrsas_cam_attach()` frees `sim_0`/`sim_1` on its internal failure paths **without
clearing `sc->sim_0` / `sc->sim_1`**. When it returns a failure, `mrsas_attach()`
(`sys/dev/raid/mrsas/mrsas.c:805` → `attach_fail_cam:` at `:838-839`) calls
`mrsas_cam_detach()`, which unconditionally re-processes the same non-NULL pointers:

```c
/* mrsas_cam.c:216-218 */
if (sc->sim_0) {
    xpt_bus_deregister(cam_sim_path(sc->sim_0));   /* UAF read of freed sim;
                                                       double-deregister */
    cam_sim_free(sc->sim_0);                        /* kfree() again */
}
```

In DF CAM, `cam_sim_free()` → `cam_sim_release()` (`sys/bus/cam/cam_sim.c:231-251`)
kfree's the sim when `refcount == 1` and drops the sim's devq reference. The
deregister-at-failure paths (`mrsas_cam.c:150-156`, `:157-165`, `:181-185`,
`:186-193`) already executed exactly that, so the second `cam_sim_free()` is a
**double free into `M_CAMSIM`**, preceded by a **use-after-free read** in
`cam_sim_path()` (`sim->path_id`) and a second `xpt_bus_deregister()` of a bus
that is already deregistered (or was never registered, on the
`xpt_bus_register`-failure path) — corrupting CAM bus bookkeeping and the
devq refcount (`cam_simq_release()` underflow → premature `cam_devq` free).

Live failure paths (all reachable under kernel memory pressure / CAM resource
exhaustion at driver attach — boot or card hotplug):

| mrsas_cam.c line | failure | frees | dangling |
|---|---|---|---|
| 150-156 | `xpt_bus_register(sim_0)` != CAM_SUCCESS | devq ref + `sim_0` | `sc->sim_0` |
| 157-165 | `xpt_create_path(&path_0)` fails | devq ref + `sim_0` | `sc->sim_0` |
| 181-185 | `xpt_bus_register(sim_1)` fails | `sim_1` | `sc->sim_1` |
| 186-193 | `xpt_create_path(&path_1)` fails | `sim_1` | `sc->sim_1` |

(The `sim_1 == NULL` path at `mrsas_cam.c:175-178` is dead code — `cam_sim_alloc()`
allocates with `M_INTWAIT` and cannot return NULL; it also leaks the caller's devq
reference.)

## How to reproduce (logic mirror, runs on the DragonFly guest)

The trigger requires an LSI/Avago Thunderbolt-MR controller *and* CAM resource
failure at attach; the audit QEMU guest has no such device (see `env.txt`), so the
kernel sequence cannot be executed there. Instead `repro_logic.c` mirrors the exact
kernel refcount/cleanup choreography (`cam_sim_alloc`/`cam_sim_release` semantics
from `sys/bus/cam/cam_sim.c:165-251` + the mrsas call sequence) and detects the
UAF/double-free:

```
./build.sh          # cc -O2 -Wall -o repro_logic repro_logic.c
./run.sh            # buggy mode: flags UAF + double free (rc=2); fixed mode: clean (rc=0)
```

Expected decisive output (`run.log`):

```
  !! sim UAF: cam_sim_path() on freed cam_sim (kernel: read of kfree'd M_CAMSIM)
  !! sim DOUBLE FREE: cam_sim_release()/kfree() on already-freed cam_sim
RESULT: 2 kernel-memory-safety violation(s) detected ...
```

## Fix

`fix.diff` — NULL out `sc->sim_0`/`sc->sim_1` after each error-path
`cam_sim_free()` (and defensively in `mrsas_cam_detach()`), plus release the
caller's devq reference on the (currently dead) `sim_1`-alloc-failure path.
