Unsigned resid underflow: ccb->csio.resid = dxfer_len - IOC-supplied TransferCount with no clamp
Summary
mpt_scsi_reply_frame_handler computes resid from the IOC's U32 TransferCount without clamping; an over-reporting IOC produces resid near 2^32, which flows to CAM and pass(4) userland and breaks the resid<=dxfer_len invariant relied on by cd/da residual and dump bookkeeping. Only TransferCount==0 under RESIDUAL_MISMATCH is special-cased. Shared with upstream FreeBSD. IOC/firmware trust class; wrong data-accounting visible to userland, mis-driven retry/dump paths; no driver memory-safety impact. Fix: clamp before subtracting.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3675 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 435 B | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| fix.diff | β | 643 B | view raw | |
| env.txt | β | 281 B | view raw | |
| verdict.json | β | 1.6 KB | view raw | |
| manifest.json | β | 467 B | view raw |
DF-3675 β unsigned resid underflow from IOC-supplied TransferCount
Severity: Low (logic/integrity; IOC-driven). ccb->csio.resid is computed as dxfer_len - TransferCount with no clamp (mpt_cam.c:3132-3133): an IOC that over-reports produces resid ~= 4G, visible to pass(4) users and violating CAM invariants for cd/da residual handling. No PoC possible without MPT hardware (env.txt). Shared with upstream FreeBSD. See VERDICT.md.
DF-3675 β VERDICT
status: inconclusive (no MPT hardware on guest; source-certain arithmetic)
Root cause
/* sys/dev/disk/mpt/mpt_cam.c:3131-3133 */
ccb = req->ccb;
ccb->csio.resid =
ccb->csio.dxfer_len - le32toh(scsi_io_reply->TransferCount);
TransferCount is a U32 field of the IOC's DMA'd SCSI_IO_REPLY frame with
no clamping: an IOC reporting more bytes transferred than the command's
dxfer_len underflows the unsigned subtraction, so CAM and userland see
resid near 4 G (dxfer_len - TransferCount mod 2^32). The driver only
special-cases TransferCount == 0 under MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH
(mpt_cam.c:3163-3176); for DATA_OVERRUN (:3198) and every other status
the bogus resid flows out untouched.
Consumers: - pass(4) copies the full CCB back to the caller β the huge resid is directly visible to the pass user (correctness/integrity of upper-layer software that trusts resid); - cd(4)/da(4) use resid for residual handling and error reporting; a resid greater than dxfer_len violates CAM's invariant and can mis-drive retry / dump accounting (e.g. da dump residual bookkeeping).
Threat: requires an IOC/firmware reporting a wrong TransferCount (or real overrun reporting) β hardware-trust class. No memory-safety consequence in the driver itself; impact ceiling is wrong data-accounting visible to userland and possibly a wedged dump/retry path. Severity Low.
Fix (also matches the defensive style used for sense data)
--- a/sys/dev/disk/mpt/mpt_cam.c
+++ b/sys/dev/disk/mpt/mpt_cam.c
@@ -3129,7 +3129,13 @@ mpt_scsi_reply_frame_handler(struct mpt_softc *mpt, request_t *req,
sstate = scsi_io_reply->SCSIState;
ccb = req->ccb;
- ccb->csio.resid =
- ccb->csio.dxfer_len - le32toh(scsi_io_reply->TransferCount);
+ {
+ uint32_t xferred = le32toh(scsi_io_reply->TransferCount);
+ if (xferred > ccb->csio.dxfer_len)
+ ccb->csio.resid = 0; /* IOC over-reported */
+ else
+ ccb->csio.resid = ccb->csio.dxfer_len - xferred;
+ }
Guest verdict
inconclusive β no MPT hardware; the underflow is proven by inspection
(unsigned U32 arithmetic, no clamp) and matches upstream FreeBSD
(mpt_cam.c ccb->csio.resid = ccb->csio.dxfer_len -
le32toh(scsi_io_reply->TransferCount); β shared bug).
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.
Fix verification
not_testableClamp diff authored (fix.diff); not exercisable without hardware.
fix.diff; env.txt
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (arithmetic proof + consumer analysis); fix.diff (clamp); env.txt (no hardware)
PoC changes
n/a
Verified recommended fix
Clamp: resid = (xferred > dxfer_len) ? 0 : dxfer_len - xferred
Verdict
Source-certain unsigned underflow: mpt_cam.c:3132-3133 computes ccb->csio.resid = dxfer_len - le32toh(TransferCount) with the IOC-supplied U32 unclamped; an over-reporting IOC yields resid near 2^32 which propagates to CAM and pass(4) userland and breaks the resid<=dxfer_len invariant relied on by cd/da residual and dump bookkeeping. Only TransferCount==0 under RESIDUAL_MISMATCH is special-cased (3163-3176). Hardware-trust threat class, no memory-safety consequence in the driver; ceiling is wrong data-accounting / wedged retry-dump paths. Not executable on this guest (no MPT HBA). Shared with upstream FreeBSD.
No comments yet.