# DF-1070 — scsi_targ_bh teardown UAF (fixed hz/2 sleep, no wakeup source)

## Verdict

**NOT REPRODUCED (runtime) — STATIC VERIFICATION CONFIRMED.**

The cited code path and bug exist verbatim in
`sys/bus/cam/scsi/scsi_targ_bh.c`. The runtime trigger requires **two**
preconditions that the audit's default guest cannot satisfy:

1. **The driver is `optional targbh`** (`sys/conf/files:46`) and is **not
   listed in `X86_64_GENERIC`** — `grep -nE 'targbh|targ_bh'
   sys/config/X86_64_GENERIC` returns nothing. Confirmed by `nm
   /boot/kernel/kernel`: **no `targbh*` symbols** in the running kernel.
   Loading it requires `kldload targbh` (root), which under the audit's
   bright-line rule is a circular precondition for a `uid0` claim.

2. Even with the module loaded, the driver auto-attaches only on a
   **target-mode-capable HBA/SIM** with `PIT_PROCESSOR` (e.g. `ahc`/`ahd`
   in target mode, or an iSCSI target SIM). The audit's QEMU guest has no
   SCSI target-mode HBA emulated (default virtio-blk + AHCI initiator
   only), so the driver's `targbhctor`/`targbhstart`/`targbhdone` paths
   are never reached.

So this is a **latent / hardware-gated** defect: real in source, present
in the source tree, but unreachable from the audit guest's default
configuration. Classified as Medium with CVSS `AV:A/AC:H` (network-adjacent
attacker, high complexity) — the AC:High reflects the narrow race window
and the specialised HBA/SIM requirement.

## Mechanism (confirmed by source trace)

`targbhdtor` (`scsi_targ_bh.c:402-424`) sets
`softc->state = TARGBH_STATE_TEARDOWN` (`:409`), calls `targbhdislun`
(`:411`), then unconditionally falls through to the default case
(`init_level == 1` after `ctor:397`) which does
`sim_lock_sleep(softc, 0, "targbh", hz/2, periph->sim->lock)` at `:420`
followed by `kfree(softc, M_SCSIBH)` at `:421`.

`grep -nE '\bwakeup\(' sys/bus/cam/scsi/scsi_targ_bh.c` returns only:

```
444:    wakeup(&periph->ccb_list);
526:    wakeup(&done_ccb->ccb_h.cbfcnp);
```

**Nobody calls `wakeup(softc)`** — the sleep always times out after
exactly 500 ms regardless of outstanding work.

`targbhdislun` (`:329-382`) aborts only `accept_tio_list` entries
(`:347-354`, via `XPT_ABORT` on each ATIO) and `immed_notify_slist`
entries (`:356-363`), then disables the LUN (`:368-371`). It **never
enumerates or aborts CTIOs** that were submitted via `xpt_action` in
`targbhstart` (`:497`). The author's `XXX` comments at `:342`
(`XXX Block for Continue I/O completion`) and `:419` (`XXX Wait for
callback of targbhdislun()`) explicitly mark this as unfinished.

If a late CTIO completes, `targbhdone` (`:518`) executes
`softc = (struct targbh_softc *)periph->softc` (`:522`) on freed memory.
The `XPT_CONT_TARGET_IO` case (`:638-695`) further dereferences
`softc->pending_queue` via `TAILQ_REMOVE` (`:648`), `softc->state`
(`:678`), and calls `targbhfreedescr` / `xpt_free_ccb` (`:692-693`).
`camperiphfree` (`cam_periph.c:600-632`) calls `periph_dtor` then
immediately frees `periph->path` and `periph` itself, so by the time the
late callback fires, both `softc` and `periph` are freed.

## Exploit chain

None developed — the driver is not present in the running kernel and the
HBA class it requires is not emulated in the audit guest. The theoretical
chain (slab-groom the freed `M_SCSIBH` allocation into a controlled object
and convert the `TAILQ_REMOVE` / function-pointer deref to code execution)
is explicitly speculative in the finding (CVSS `AV:A/AC:H`, confidence
`speculative`).

## PoC

`verify.sh` — static-verification script that walks the cited path with
`grep`/`sed` against `sys/`, confirming: (1) the fixed `hz/2` sleep +
immediate `kfree(softc)` at `:420-421`; (2) **no** `wakeup(softc)` in the
file (only `wakeup(&periph->ccb_list)` and `wakeup(&done_ccb->ccb_h.cbfcnp)`);
(3) `targbhdislun` aborts ATIOs and IMMED_NOTIFY but never CTIOs; (4)
`targbhdone` dereferences `periph->softc` unconditionally (`:522`); (5)
the driver is `optional targbh` and absent from `X86_64_GENERIC`. Run from
the repo root: `sh findings/poc/DF-1070/verify.sh`.

## Fix

`fix.diff` — adds an `outstanding_ctios` counter to `targbh_softc`,
increments it in `targbhstart` before `xpt_action`, decrements it (with
`KKASSERT` + `wakeup_one`) in `targbhdone`'s `XPT_CONT_TARGET_IO` case
after `xpt_release_ccb`, and replaces the fixed `hz/2` sleep in
`targbhdtor` with a `while (softc->outstanding_ctios > 0)
sim_lock_sleep(&softc->outstanding_ctios, ..., hz, ...)` loop. This
closes the race by blocking teardown until every in-flight CTIO has
called back, while still allowing a periodic 1 s wakeup to re-check.
**Matches** the finding markdown's recommended fix (it proposed the same
counter + condition-wait pattern).

## Reproduce

```
sh findings/poc/DF-1070/verify.sh     # static source verification
```
