# DF-5593 VERDICT — REPRODUCED (deterministic kernel heap leak)

## What was run

ptsim3.ko (the DF-5592 rig, mode 3) on the stock guest kernel: register a bus
with production `cam_sim_alloc`/`xpt_bus_register`, run a bus scan (probe
machinery allocates ccbs via `xpt_get_ccb` from the sim's pool and returns
them to `sim->ccb_freeq` via `xpt_release_ccb`), then the standard driver
detach: `xpt_bus_deregister()` + `cam_sim_free()`.

## Observations (run.log)

```
[PTSIM3] DF-5593: sim->ccb_count=8, ccb_freeq length=8 (each orphaned union ccb ~720 bytes + callout, M_CAMXPT)
[PTSIM3] DF-5593: clean detach (deregister+free): the 8 pooled ccbs are NOT freed by cam_sim_free()
[PTSIM3] DF-5593: detach DONE; 8 ccbs orphaned -- check 'vmstat -m | grep -i xpt' delta
```
vmstat -m around the cycle:
```
CAM_XPT  40  7.71K  ... 275   (before scan)
CAM_XPT  69  19.2K  ... 453   (after scan+detach: count never returns)
```
The module-side counter is the decisive number: 8 ccbs were on
`sim->ccb_freeq` at the moment `cam_sim_release()` kfree'd the sim, and no
code path exists that frees them afterwards (`xpt_free_ccb` requires going
through live-sim accounting at cam_xpt.c:4263-4271; the sim is gone).

## Root cause

cam_sim.c:246-252 — final branch releases devq + kfree(sim) only; `ccb_freeq`
(head pointer dies with the sim), `ccb_count`, and the pooled ccbs are never
visited. Pool filling is normal CAM behavior (`sim->max_ccbs` grows with each
attached device, cam_xpt.c:5156), so any real controller that has served I/O
has a non-empty pool at detach.

## Impact

Kernel heap leak, ~0.75-1 KB per pooled ccb per controller detach. Trigger:
kldunload / device removal / surprise removal — privileged or hardware-event
driven. Repeated cycles (driver reload churn, hot-plug racks) exhaust kernel
memory → eventual allocation failures/DoS. Not an info leak; classified
impact=leak, severity Medium (Low-Medium band; deterministic and permanent).

## Fix

Drain `sim->ccb_freeq` in `cam_sim_release()`'s final branch before
`kfree(sim)` (README). Not rebuild-validated in this run (leak-class finding;
the validated rebuild in the DF-5592 pack exercises the same free path and
does not conflict — the drain slots straight into the same branch after
`xpt_sim_finalize()` succeeds). fix_status: not_tested.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

(No fix.diff for this item: documented recommendation only - see Fix above.)
