# DF-3823 — asr(4) maps the adapter-supplied FromFIFO reply offset into a kernel pointer with no validation (device-controlled arbitrary-kernel deref/write), and reads/writes MMIO at device-controlled frame offsets

- **File:** `sys/dev/raid/asr/asr.c`
- **Lines:** 2886-2898 (reply mapping), 2906-2953 (FAIL path MMIO
  offsets)
- **Class:** CWE-129/CWE-20 — unchecked device index → wild kernel
  pointer → arbitrary read/write (device-trust)
- **Reach:** adapter interrupt only (I2O outbound FIFO).  No software
  path produces the value.

## Analysis

`asr_intr()`:

```c
2886:		if (((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE)
2887:		 && ((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE)) {
2888:			break;
2889:		}
2890:		Reply = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)(ReplyOffset
2891:		  - sc->ha_Msgs_Phys + (char *)(sc->ha_Msgs));
```

`ReplyOffset` is whatever the IOP posts in the outbound FIFO.  It is
*assumed* to lie inside the `ha_Msgs` reply-frame pool
(`contigmalloc`'d at asr.c:1910-1914, `ha_Msgs_Count` frames), but
nothing checks:

```c
ReplyOffset >= sc->ha_Msgs_Phys
 && ReplyOffset <  sc->ha_Msgs_Phys + ha_Msgs_Count * sizeof(frame)
```

An offset outside that window makes `Reply` an arbitrary offset from the
pool base — including *negative* (below the pool).  The handler then:

- reads the 64-bit `InitiatorContext64` from `Reply->…StdMessageFrame`
  (asr.c:2896-2898) and treats it as a `union asr_ccb *`;
- passes that pointer to `ASR_ccbRemove()` (asr.c:2971) which does
  `callout_stop(&ccb->ccb_h.timeout_ch)` and `LIST_REMOVE` — two
  write primitives through the forged pointer;
- writes completion status into `ccb->ccb_h.status`, `csio.scsi_status`,
  `resid`, and `bcopy`s sense data from `Reply->SenseData`
  (asr.c:2975-3052);
- finally `xpt_done(ccb)` / `wakeup(ccb)`.

In the message-failure branch (asr.c:2899-2954) it additionally uses the
device-supplied `PreservedMFA` (`MessageOffset`, asr.c:2906-2908) as a
bus-space offset for a 4-byte MMIO **read** (asr.c:2917-2921) and an
MMIO **write** of a whole `I2O_UTIL_NOP_MESSAGE` (asr.c:2948-2949) —
unbounded offsets into the frame BAR (device-side corruption / potential
host bus faults).

## Impact ceiling

With a compromised or faulty IOP, this is a full arbitrary
kernel-read/write engine: choose `ReplyOffset` so `Reply` lands on
controlled DMA'd memory (or anywhere in physmap), return a crafted
`InitiatorContext64`, and `ASR_ccbRemove` performs unlink-style writes at
the chosen address before an indirect dispatch through the CAM layer.
Severity capped Low for the hardware precondition.

## Verdict

`inconclusive` — the audit guest has no DPT/Adaptec I2O HBA
(see ../DF-3825/run.log and ../DF-3819/env.txt), so `asr_intr` never
runs.  Source-level: certain.
