# DF-1045 PoC — SCSI target driver targclose UAF

## Trigger

Two variants of a single UAF in `targclose`, both reachable from any
local user holding `SYSCAP_RESTRICTEDROOT` (i.e. real root outside
jail/chroot, which is what `targopen:177` requires).

### Variant A — success-path UAF

`open(/dev/targN) + TARGIOCENABLE(path_id, target_id, lun_id)` on a
target-capable HBA, then `close()`. The close sequence is:

  `targclose` → `targdisable` (aborts any pending ATIOs back to
  `user_ccb_queue`) → `kfree(softc)` at line 240 → `cam_periph_release`
  → `targdtor` at line 528 → `TAILQ_FIRST(&softc->user_ccb_queue)` on
  freed memory at line 541.

### Variant B — early-exit / cross-instance UAF (no CCBs in flight)

Process A: `open + TARGIOCENABLE + TARGIOCDISABLE + close`. Close takes
the early-exit at line 216 (state lacks `TARG_STATE_LUN_ENABLED`, periph
!= NULL) and kfrees softc while leaving `periph->softc` dangling in the
still-registered periph.

Process B: `open(/dev/targM) + TARGIOCENABLE(same path X)`. `targenable`
at line 446 retrieves `del_softc = periph->softc` (already freed by A)
and at line 447 dereferences `del_softc->state` → page fault.

## Build & run

```
cc -o tgt_uaf tgt_uaf.c
sudo ./tgt_uaf <path_id> <target_id> <lun_id>                 # variant A
sudo ./tgt_uaf <path_id> <target_id> <lun_id> --disable-then-close   # variant B step 1
sudo ./tgt_uaf <path_id> <target_id> <lun_id>                 # variant B step 2
```

`path_id`, `target_id`, `lun_id` come from `camcontrol devlist` and must
match a path on a SCSI HBA whose driver advertises the `PIT_PROCESSOR`
flag (so that `TARGIOCENABLE` succeeds). Examples that work in DragonFlyBSD:
`ahc(4)`, `ahd(4)` with target mode, or `mpt(4)`.

## Expected output

```
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x<address drawn from freed M_TARG slab>
targdtor(...) at scsi_target.c:541
camperiphfree(...) at cam_periph.c:370
cam_periph_release(...) at cam_periph.c:600
targclose(...) at scsi_target.c:245
spec_strategy(...) at ...
... close() syscall path ...
```

On `INVARIANTS` / `DEBUG` kernels with slab poisoning, the fault virtual
address will contain poison bytes (e.g. `0xDE…`). On a stock kernel, slab
grooming can convert the UAF into a free-of-attacker-controlled-address
primitive via `targfreeccb` (which calls `kfree` on
`ccb->ccb_h.targ_descr` and on the ccb itself).

## Making the bug self-evident

If you cannot run a live HBA target-mode test, add the following KASSERT
to `sys/bus/cam/scsi/scsi_target.c` just before line 534 and rebuild:

```c
KASSERT(periph->softc != NULL, ("targdtor: softc UAF"));
```

The assertion fires deterministically on the first `close()` after a
successful `TARGIOCENABLE`.

## Kernel references

- `sys/bus/cam/scsi/scsi_target.c:207-248` — `targclose` (kfree before release)
- `sys/bus/cam/scsi/scsi_target.c:216-222` — early-exit branch (disable-then-close)
- `sys/bus/cam/scsi/scsi_target.c:240` — kfree(softc)
- `sys/bus/cam/scsi/scsi_target.c:245` — cam_periph_release → targdtor
- `sys/bus/cam/scsi/scsi_target.c:442-456` — `targenable` (early-exit UAF)
- `sys/bus/cam/scsi/scsi_target.c:527-553` — `targdtor` (deref of freed softc)
- `sys/bus/cam/cam_periph.c:370-374, 573-578, 600-601` — release lifecycle
