# DF-2523 — PoC evidence pack (verify run, 2026-10-14)

Finding: `sys/dev/disk/trm/trm.c` — `trm_Reselect()` walks the circular
DCB list looking for the reselecting target/LUN with **no exit** when
nothing matches (trm.c:2428-2431):

```c
pDCB = pACB->pLinkDCB;
while (RselTarLunId != *((u_int16_t *) &pDCB->TargetID)) {
	pDCB = pDCB->pNextDCB;
}
```

`RselTarLunId` is a 13-bit register value (`& 0x1FFF`, trm.c:2426) while
a DCB can only ever pack to `(LUN<<8)|ID <= 0x070F` — a reselection with
any higher bit set (malicious or glitching SCSI device driving the
reselection phase) matches nothing and the loop spins forever at
interrupt context.

## How it was verified

Same production-handler rig as DF-2522/24 (`df252x_trm_rig.c`, zero
driver code): fabricated post-attach ACB with a circular 3-DCB ring
(ID/LUN = 1/0, 2/0, 3/2), the TRM-S1040 register file emulated behind
the kernel's own `bus_space` accessors, and the **running kernel's own
`trm_Interrupt()`** dispatched with `INT_RESELECTED`.

* control (`go=4`): `TRMREG_SCSI_TARGETID=0x0203` matches ring[2] —
  production `trm_Reselect` completes (pActiveDCB=ring[2], TmpSRB armed
  with `SRB_UNEXPECT_RESEL`, `MSG_ABORT` in MsgOutBuf, `DO_DATALATCH`
  written) and **returns**;
* DF-2523 (`go=5`): `TRMREG_SCSI_TARGETID=0x1000` — matches no DCB.
  Dispatch runs in a dedicated kernel thread (`df2523_t1`) with 3s/6s/9s
  heartbeat callouts.

## What the run showed (stock kernel)

```
df2523[T1]: entering production trm_Interrupt (RESELECTED,
            RselTarLunId=0x1000 matches NO DCB of the 3-DCB circular list)
df2523[HB]: *** NON-TERMINATION CONFIRMED: 3.-08038 s inside
            trm_Interrupt/trm_Reselect - the DCB ring walk
            (trm.c:2428-2431) has no exit ***
df2523[HB]: *** NON-TERMINATION CONFIRMED: 6.-07982 s ... ***
df2523[HB]: *** NON-TERMINATION CONFIRMED: 9.002041 s ... ***
```

The "RETURNED" marker never printed. Within ~1 minute the **whole guest
wedged**: ssh stopped answering (connection banner timeout), the serial
console stopped echoing, no kernel panic was printed, and recovery
required killing QEMU (`vm.sh down`: "guest not answering (likely DDB on
panic)"). An unkillable kernel thread spinning in production code starves
DragonFly's per-CPU user scheduler — the same non-termination is a
hard, unrecoverable lockup on real hardware where the loop runs in the
interrupt handler at crit priority (clock included).

## Build / run (in guest, as root) — run LAST, the guest does not recover

```
sh build.sh
kldload trm
sh run.sh          # control(4) returns; DF-2523(5) wedges the guest
```

## Fix validation

`fix.diff` bounds the walk by `pACB->DeviceCnt` (the driver's own ring
bookkeeping) and, when the reselection matches no DCB, drops the
connection (`STOPDMAXFER` + `DO_CLRFIFO|DO_HWRESELECT`) and returns
instead of spinning. Validated on the rebuilt kernel: `go=5` returns
immediately (`df2523[T1]: ... RETURNED`), no heartbeat fires, the guest
stays healthy; the matching control still completes exactly as before.
