# DF-3824 — ASR_init_message() writes the mandatory sense SGL element 8 bytes past asr_action()'s 512-byte stack buffer when a single CCB spans ≥58 physical runs (and the adapter's SG limit is ignored entirely)

- **File:** `sys/dev/raid/asr/asr.c`
- **Lines:** 1812-1847 (`ASR_init_message` SGL loop + sense element),
  2680-2682 (the 512-byte stack buffer `struct Message { char
  M[MAX_INBOUND_SIZE]; }`), 2459-2465 (`ha_SgSize` computed but **never
  used**)
- **Class:** CWE-787 stack OOB write (kernel), 8 bytes
- **Reach:** `XPT_SCSI_IO` with `csio.dxfer_len` spanning ≥ 58
  physically-discontiguous runs (≥ ~232 KiB at 4 KiB pages).

## Layout (verified by executing sg_layout_proof.c on the audit guest)

```
offsetof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE, SGL) = 48
sizeof(I2O_SGE_SIMPLE_ELEMENT)                =  8
SG_SIZE (asr.c:258)                           = 58
asr_action stack buffer                       = 512 (MAX_INBOUND_SIZE)
loop fills SGL elements [0..57]  -> bytes [48, 512)
sense element (always written after the loop, asr.c:1844-1845)
            at &SGL.u.Simple[58] -> bytes [512, 520)   <-- 8 bytes past
```

The loop bound `sg < &SGL.u.Simple[SG_SIZE]` (asr.c:1812-1813) lets the
data elements consume the *entire* 512-byte buffer; the sense element
`SG(sg, 0, …, &ccb->csio.sense_data, span)` (asr.c:1841-1845) is then
written unconditionally at element 58 — offsets 512..519 — past
`struct Message M[512]` on `asr_action()`'s stack.

The overwritten 8 bytes are `FlagsCount`
(`I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT|LAST_ELEMENT|EOB`|flags, plus
`Count = span`) and `PhysicalAddress = vtophys(&ccb->csio.sense_data)`
— a low-32-bit physical address — written over whatever local/saved
state the compiler placed after `Message`.

## Reachability

- Buffer-cache disk I/O is capped at `MAXPHYS = 128 KiB`
  (sys/cpu/x86_64/include/param.h:122) → at most ~33 physical runs →
  loop uses ≤ 33 elements → sense lands in-bounds.  **Not reachable via
  normal disk I/O.**
- CAM pass-through (`camcontrol` on `/dev/passN`, root-only device) can
  submit a CCB with arbitrary `dxfer_len` and a user-malloc'd, hence
  physically-scattered, buffer: a 256 KiB+ transfer trivially produces
  ≥58 runs → the overflow fires.
- Independently, the SGL bound also ignores `sc->ha_SgSize` (computed
  from the adapter's `InboundMFrameSize` at asr.c:2459-2465, referenced
  nowhere else in the driver) and `I2O_MESSAGE_FRAME_getMessageSize()`
  grows past what the adapter's inbound frame can hold — device-side
  frame overflow on every adapter whose frame is smaller than 512+ bytes.

## Verdict

`inconclusive` for the kernel write itself — the audit guest has no
asr(4) HBA (see ../DF-3825/run.log), so `asr_action`'s XPT_SCSI_IO path
never executes; the *arithmetic* was verified by compiling and running
`sg_layout_proof.c` on the guest (`run.log` in this pack).  Source-level
confidence: certain.
