# DF-1361 — VERDICT

**REPRODUCED at the function level** (impact: `leak`).

## Mechanism

mps_user_pass_thru() at mps_user.c has two parallel blocks (SCSI-IO path at :858 and the default path at :1019) that validate 'if (sz > data->ReplySize) err=EINVAL; else copyout(cm->cm_reply, user, data->ReplySize)'. sz = rpl->MsgLength * 4 is the actual reply size, computed at :1010. The copyout uses ReplySize (user uint32) instead of sz — so when sz <= ReplySize (the valid case), the copyout reads ReplySize bytes from cm->cm_reply (which points into the reply_frames DMA pool) past the actual reply into adjacent DMA frames / kernel heap. Twin of DF-1327 (mpr_user.c).

## Why not live-reproduced on the QEMU guest

LSI MPT-Fusion 2 (mps) hardware absent from QEMU guest. The mps module loads only on matching LSI HBA. Reachable via the mps ioctl by an authenticated local user (operator).

## Recommended fix

In both copyout branches, replace 'copyout(cm->cm_reply, ..., data->ReplySize)' with 'copyout(cm->cm_reply, ..., sz)'. sz is already computed as rpl->MsgLength * 4 in the default path; the SCSI-IO path must compute sz before the copyout.

## Kernel references (confirmed during verification)

- sys/dev/raid/mps/mps_user.c:858-859 (SCSI-IO path: copyout with ReplySize)
- sys/dev/raid/mps/mps_user.c:1010 (sz = rpl->MsgLength * 4)
- sys/dev/raid/mps/mps_user.c:1019 (default path: copyout with ReplySize instead of sz)

## Build/run

- Build harness: `cc -O2 -Wall -o trigger trigger.c`
- Run harness: `./trigger`
- Apply fix: `cd /usr/src && patch -p1 < fix.diff`
- Build single-fix kernel: `make -j6 nativekernel KERNCONF=X86_64_GENERIC`
  (validated — see `fix_build.log`; all 15 fixes compile cleanly in one batched
  build, rc=0).

## Tested kernels

- baseline: `DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64`
- patched : `DragonFly 6.5-DEVELOPMENT #1: Mon Jul 20 21:51:01 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64`
