# DF-4834 — VERDICT

**REPRODUCED (harness; the guest has no LSI MegaRAID controller — same
methodology as DF-4559/DF-4363).**

## The code path (traced)

1. `mrsas_ioctl()` — `sys/dev/raid/mrsas/mrsas.c:1112-1159`. `arg = ap->a_data`
   (line 1116) is the raw userland pointer of `ioctl(2)`; line 1151 hands it
   to `mrsas_passthru(sc, (void *)arg)` **with no copyin**.
2. `mrsas_passthru()` — `sys/dev/raid/mrsas/mrsas_ioctl.c:148-370`.
   `user_ioc` therefore points into *user* address space for the whole call:
   - :172 `in_cmd->dcmd.opcode` — first raw user deref (unmapped arg ⇒
     unprotected kernel page fault ⇒ panic; on SMAP-enabled CPUs every call
     faults).
   - :174 `user_ioc->frame.hdr.cmd_status = MFI_STAT_OK` — raw kernel write
     into user memory (RO mapping ⇒ panic).
   - :186 validation `user_ioc->sge_count > MAX_IOCTL_SGE (16)` reads user mem.
   - :153-156 stack arrays `ioctl_data_tag[16] / ioctl_data_dmamap[16] /
     ioctl_data_mem[16] / ioctl_data_phys_addr[16]`.
   - :225-264 the copyin loop **re-reads** `user_ioc->sge_count` every
     iteration and writes `&ioctl_data_tag[i]` (:237), `&ioctl_data_mem[i]`
     (:241-242), `&ioctl_data_phys_addr[i]` (:246-248), plus
     `kern_sge32[i]` (:254-255 — into the 1024-byte frame DMA block).
   - :301 `mrsas_issue_blocked_cmd()` — the firmware round-trip (up to 180 s
     wait budget, MRSAS_INTERNAL_CMD_WAIT_TIME=180) — a *very* wide window.
   - :307-314 copyout loop — **second** re-read of `sge_count`/`sgl[]`;
     reads `ioctl_data_mem[i]` OOB and uses the stack value as the `copyout`
     source (`copyout` validates only the destination,
     sys/platform/pc64/x86_64/support.s:228-258 — a valid kernel pointer as
     source becomes a kernel-memory disclosure).
   - :354-364 cleanup loop — **third** re-read; `bus_dmamap_unload(ioctl_data_tag[i], ...)`
     with OOB-read stack garbage.
3. The ioctl is reachable by root and the `operator` group
   (`make_dev(..., UID_ROOT, GID_OPERATOR, 0660)`, mrsas.c:790-792).

## What the harness proves (run.log)

- **arm 1** — flip `sge_count` 16→0x20000 right after the check: the loop
  writes `ioctl_data_tag[i]/ioctl_data_mem[i]` out of bounds until it hits the
  guard page (SIGSEGV). Canary slots 16..591 recorded hits — in the kernel
  those slots are the rest of `mrsas_passthru`'s frame (adjacent locals, saved
  registers, return address) and the write value is a heap pointer
  (bus_dma_tag). Attacker-chosen offset: 8·i bytes, i up to 2^32-1.
- **arm 2** — deterministic mid-command flip: the copyout loop reads
  `ioctl_data_mem[16..23]` past the array and uses the (kernel stack) values
  as copyout sources — the leak/wild-deref class, no tight race required.
- **arm 3** — kernel-copy model (the fix): racer is invisible, loops stay
  bounded, zero writes past the arrays.

## Escalation ceiling (honest)

Primitive = controlled-offset kernel stack OOB write of kernel heap pointers +
OOB stack reads (disclosure when a valid pointer sits in the slot) + wild
`bus_dmamap_unload` on garbage. On this non-SMEP/no-KASLR target a full
stack-pivot chain is plausible, but the ioctl requires root/operator and the
guest has no mrsas device, so end-to-end uid=0 could not be demonstrated —
same limitation recorded for the pass-1 family (DF-1917..DF-1919, PR:L per
this audit's convention).

## Fix

Copy the packet in at the ioctl entry and operate on the kernel copy
(`fix.diff`, kernel-copy model validated by harness arm 3).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
