# DF-2427 PoC — OOB array access in amd_Reselect() on garbage reselect ID (amd.c:1829-1844)

* File under test: `sys/dev/disk/amd/amd.c` (AMD53c974 / Tekram DC-390(T) SCSI HBA driver)
* Bug: `amd_Reselect()` decodes the reselecting-target ID from the SCSI
  FIFO as `ffs(raw ^ HostID_Bit) - 1` with no check that `ffs()` was
  non-zero (amd.c:1831). A reselection byte that XORs with HostID_Bit
  to **zero** yields `cur_target = -1` (cur_target is a signed int,
  amd.h:204); the subsequent `tinfo[-1]`, `untagged_srbs[-1][lun]`,
  `disc_count[-1][lun]` accesses (amd.c:1833-1835, 1844-1845) read and
  WRITE outside the per-target arrays in `struct amd_softc`. A
  multi-bit garbage byte decodes to a WRONG but in-range target
  (confused deputy: the reselection nexus binds to another target's
  SRB).
* Attacker: a malicious (or signal-garbled) SCSI TARGET device on the
  bus of this HBA drives the reselection phase. No software-only path
  exists; the trigger is register/FIFO-level wire input.

## Harness (shared with DF-2426)

Kernel-compiled verbatim interposition: `df_pre.c + <verbatim amd.c> +
df_post.c` compiled into X86_64_GENERIC as `dev/disk/amd/
dfamd_harness.c` (sha256-gated). Register access executes the real
inline `bus_space` code against a fabricated memory-mapped AMD53c974
register block + SCSI FIFO; the scenarios enter through the REAL
`amd_intr()` (RESELECTED interrupt) → `amd_Reselect()`.

## Build / run

```
cd findings/poc/DF-2426 && ./build.sh     # shared baseline kernel
cd ../DF-2427 && ./run.sh                 # battery=1 contained, battery=2 fault
cd ../DF-2426 && ./make_fixed.sh          # apply both fix.diffs, rebuild
cd ../DF-2427 && ./run_fixed.sh           # all scenarios rejected, no damage
```

Expected on the verbatim kernel:

* battery=1, scenario A (poison byte 0x80 = HostID_Bit, lun 0):
  `cur_target = -1`; `untagged_srbs[-1][0]` is **softc-8** — the OOB
  READ loads a wild `active_srb` from the 8 bytes before the softc, the
  OOB WRITE NULLs those bytes; `disc_count[-1][0]--` corrupts 16 bits
  of the tinfo tail (tinfo[6].goal); chip config registers are loaded
  from `tinfo[-1]` (OOB bytes of untagged_srbs[7][6..7]); a follow-on
  MSG IN writes `SRB_DISCONNECT` through the wild `active_srb` into a
  victim object.
* battery=1, scenario A2 (lun 6): `untagged_srbs[-1][6]` aliases
  `&cur_target/cur_lun` — wild non-canonical pointer + both fields
  zeroed.
* battery=1, scenario B (byte 0xA8, attacker at ID 5): decodes target
  3; target-3's SRB is bound as active_srb (SRB/nexus confusion).
* battery=1, scenario C (byte 0xA0): correct in-bounds binding (control).
* battery=2: scenario A2 then lets the real MSG IN path dereference
  the non-canonical active_srb → **kernel fault** captured on the
  serial console.

Expected on the fixed kernel (reject non-single-bit XOR remainder,
reset bus): every scenario prints `FIXED behavior`, canary rings and
tinfo tail untouched, victim object untouched, no fault.

## Fix

Validate the decoded target before any array use — see `fix.diff`
(reject zero/multi-bit remainder of `raw ^ HostID_Bit`, reset the bus).
