# DF-3896 — Unsigned resid underflow: csio->resid = cm_length − IOC-supplied TransferCount with no clamp (UNDERRUN path)

File: `sys/dev/raid/mps/mps_sas.c` (pass 2). Twin of DF-3675 (mpt_cam.c).

## What

```c
	case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
		csio->resid = cm->cm_length - rep->TransferCount;   /* mps_sas.c:1943 */
```

`cm->cm_length` is `u_int` (mpsvar.h:231) set from `csio->dxfer_len`
(mps_sas.c:1744); `rep->TransferCount` is a firmware-supplied U32 taken
verbatim from the DMA'd reply frame. If the IOC ever reports
TransferCount > cm_length (malformed/compromised controller or firmware
bug during error recovery — precisely the environment UNDERRUN replies
appear in), the subtraction wraps and `csio->resid` becomes a huge value
(e.g. 0xFFFFFFF0). CAM periphs and userland then believe nearly nothing
was transferred. resid is consumed by da(4)/cd(4) error handling and
exposed via csio → userland (e.g. pass(4) CAM IOCBs); a bogus huge resid
corrupts data-integrity decisions rather than memory.

Sibling reference: DF-3675 fixed the identical pattern in mpt_cam.c by
clamping. The DATA_OVERRUN case in the same function sets resid = 0
(mps_sas.c:2083) and the default path sets resid = cm_length
(mps_sas.c:2114) — only the UNDERRUN path does unchecked arithmetic.

## Build/run

`run.sh` records the hardware gate.

## Fix

Clamp: `csio->resid = (rep->TransferCount < cm->cm_length) ?
cm->cm_length - rep->TransferCount : 0;` (see fix.diff).
