# DF-1356 — VERDICT

**Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest; the device is root-only even with hardware).**

## Mechanism (source trace)

`amr_ioctl()` handles `AMR_CMD_PASS` (SCSI passthrough) at
`sys/dev/raid/amr/amr.c:825-849`.

1. **Attacker-controlled length** — `amr.c:832`:
   ```c
   len = au_cmd[2];                 /* u8, user-supplied, 0..255 */
   _ap->ap_cdb_length = len;
   bcopy(au_cmd + 3, _ap->ap_cdb, len);   /* amr.c:834 — the overflow */
   ```
   `au_cmd` is the user's `struct amr_user_ioctl.au_cmd[32]` (`amrio.h:99`).
   `len` is never validated against the destination size.

2. **Undersized destination** — `amrreg.h:489`:
   ```c
   u_int8_t  ap_cdb[AMR_MAX_CDB_LEN];   /* AMR_MAX_CDB_LEN = 0x0a = 10  (amrreg.h:90) */
   ```
   `ap_cdb` is a fixed 10-byte field inside `struct amr_passthrough`
   (`amrreg.h:478`, 62 bytes `__packed`), which lives inside `union amr_ccb`
   (`amrvar.h:110`), a **128-byte DMA-coherent allocation**.

3. **Overflow** — with `len = 255`, `bcopy` writes 255 bytes starting at
   `ap_cdb[0]`: 245 bytes run past `ap_cdb[10]`, overwriting
   `ap_request_sense_length`, `ap_request_sense_area[32]`, `ap_data_transfer_address`
   (the DMA pointer), `ap_data_transfer_length`, then the remainder of the
   128-byte `ccb` union, and finally the **adjacent `ccb` in the command cluster**
   (`amr_alloccmd_cluster` allocates an array of `union amr_ccb`). The content is
   fully attacker-controlled (the user's `au_cmd` buffer). The same unchecked
   `len` is also used at `amr.c:837-842` to index `au_cmd[len+3..len+6]`, which
   overreads the 32-byte `au_cmd` — a secondary source overread.

Sibling of DF-1235/DF-1270/DF-1348 (the same CDB-overflow class in the amr
direct/ext passthrough paths).

## Reachability on this guest

`amr` is a `device` in `X86_64_GENERIC` (compiled into the kernel), but it is a
PCI driver for LSI/AMI MegaRAID controllers. The audit guest is a QEMU/KVM VM
with **no MegaRAID HBA** (`pciconf -l` shows only virtio devices), so the driver
never attaches, no `/dev/amrN` node is created (`amr.c:261 make_dev` is never
called), and `amr_ioctl` is unreachable at runtime.

**Privilege model** (`amr.c:261`):
```c
make_dev(&amr_ops, ..., UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "amr%d", ...);
```
Mode is `S_IRUSR|S_IWUSR` (0600) — **owner (root) only**; the `GID_OPERATOR`
group is granted no bits, so even operator-group users cannot open it. This is
therefore a **root→kernel** path: on an amr-equipped host only root can reach the
overflow. Root→kernel corruption is a hardening gap, not an unprivileged
escalation (root already wins). There is no unprivileged path to `amr_ioctl` on
this guest (no device), and the device is root-only even with hardware.

Phase-4(d): real code path, unreachable on this guest due to absent hardware;
primitive proven at the object/harness level.

## Primitive characterization

- **Write size:** up to 255 bytes from a 10-byte field (245-byte overflow).
- **Content control:** fully attacker-controlled (user's `au_cmd` bytes).
- **Target:** `union amr_ccb` 128-byte DMA-coherent slab; overflow reaches the
  adjacent `ccb` and DMA/SG memory.
- **Corrupts:** `ap_data_transfer_address` (DMA pointer), sense area, and the
  next `ccb` — on a real controller a malicious CDB could redirect DMA or
  corrupt a sibling command.

## Harness proof

`harness.c` allocates two adjacent 128-byte `union amr_ccb`s plus a canary
guard, replays `amr.c:832-834` with `au_cmd[2]=255`, and reports the overflow.
Output (`run.log`):
```
[DF-1356] attacker len=255 (au_cmd[2]); AMR_MAX_CDB_LEN=10 -> 245 bytes overflow past ap_cdb
[DF-1356] ccb0.ap_data_transfer_address now = 0x41414141 (DMA ptr corrupted)
[DF-1356] bytes written past ap_passthrough but inside ccb0: 66
[DF-1356] ADJACENT ccb1 corrupted: YES -> next DMA ccb overwritten (128 attacker bytes landed in it)
[DF-1356] guard region past ccb1 corrupted: YES (5 bytes)
```

## Exploit chain / escalation

Write-capable primitive, but it fires **only inside a running kernel with an
amr-attached controller opened by root**. On this guest there is no controller
(root or otherwise), so the chain cannot be demonstrated in-kernel. Even on a
host with the controller the path is root-only (`0600`), so there is no
privilege boundary to cross (root→root). The honest reported impact is the
corruption primitive itself, confirmed at the object/harness level.

## Fix

`fix.diff` clamps `len` to `AMR_MAX_CDB_LEN` immediately after reading
`au_cmd[2]`, before both the `bcopy` (closes the destination overflow) and the
`au_cmd[len+3..]` reads (closes the source overread):
```c
len = au_cmd[2];
_ap->ap_cdb_length = len;
if (len > AMR_MAX_CDB_LEN)
    len = AMR_MAX_CDB_LEN;
bcopy(au_cmd + 3, _ap->ap_cdb, len);
```
**Validated:** `patch -p1 --dry-run` succeeds (hunk @831), and `amr.ko` builds
clean with a clean module build (`rc=0`, no errors/warnings — `fix_build.log`).
Matches the finding's proposed fix (`check len <= AMR_MAX_CDB_LEN`).

## Fix-validation status

`not_testable` for a *live* kernel before/after (the driver cannot attach on the
guest). Evidence the fix is correct: (1) the harness shows clamping keeps the
write inside `ap_cdb[10]`; (2) the fix compiles cleanly in-tree (amr.ko
produced); (3) the clamp also closes the secondary `au_cmd[len+N]` source
overread.
