β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2530

Unclamped sense_len overflows 32-byte inline sense_data buffer on CHECK CONDITION

Summary

On SCSI CHECK CONDITION vpo_intr issues REQUEST_SENSE using csio->sense_len (u_int8_t 0..255) as byte-transfer bound and &csio->sense_data as destination but csio->sense_data is fixed 32-byte struct scsi_sense_data. Low-level loops write up to blen bytes into buffer. sense_len>32 overflows sense_data into adjacent csio fields (cdb_len sglist_cnt scsi_status sense_resid resid cdb_io msg_ptr). csio->sense_len reachable at arbitrary value via CAM pass-through xpt_merge_ccb copies wholesale no validation. Local privileged attacker root/operator /dev/passN. Bounded CCB corruption not arbitrary write. [PASS-2 NOTE: DF-2530/DF-2531 are the sense/cdb halves of DF-1958 - one unclamped-length root; fold on triage] [VERIFIED pass2: reproduced - sense_len 255 written into 32-byte sense_data destroys CCB markers cdb_len sglist_cnt scsi_status with 223 bytes past the field; clamp fix A/B clean]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2530 Β· 8 files
FileTypeDescriptionSize
README.md β€” 967 B ↓ raw
VERDICT.md β€” 2.1 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 460 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 548 B view raw
manifest.json β€” 687 B view raw
verdict.json β€” 2.4 KB view raw

DF-2530 -- Unclamped sense_len overflows the 32-byte sense_data on CHECK CONDITION

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_vpo (rig.c = harness, gen.sh = verbatim extractor, dfv_*_extract.h = generated production text). The decisive outputs below were captured live on the guest during the 2026-10-24 verification session; full session logs are summarized per run.

Build

sh build.sh            # extracts verbatim driver text + builds the KLD

Run (guest, root)

kldload ./dfv_rig.ko

kldload dfv_vpo.ko; sysctl kern.dfv_vpo_run=1; sysctl -n kern.dfv_vpo_status

Expected

run.log: 255 bytes written, markers destroyed; control 32/intact; fixed-caller model intact.

Fix validation

fix.diff clamps the REQUEST_SENSE length to sizeof(csio->sense_data) in vpo_intr before the call; A/B clean.

Verdict: reproduced / impact=limited_write (see verdict.json, VERDICT.md)

VERDICT.md
↓ download raw

DF-2530 verdict -- Unclamped sense_len overflows the 32-byte sense_data on CHECK CONDITION

status: reproduced reproduced: 1 impact: limited_write confidence: certain attempts: 2 runtime_sec: 700

vpo_intr hands csio->sense_len (user CCB byte, 0..255 via pass(4) xpt_merge_ccb) and &csio->sense_data (32 bytes) to the transfer engine; the verbatim imm_do_scsi wrote 255 bytes (count=255) into the 32-byte field - the rig dump shows sense bytes 28..39 all 0x5A and the post-CCB markers destroyed: cdb_len=0x5a sglist_cnt=0x5a5a scsi_status=0x5a sense_resid=0x5a resid=0x5a5a5a5a (pre-set markers 11/22/33/44/55) - 223 bytes past the field into the rest of union ccb, exactly the field list in the finding. Control (sense_len=32) leaves the markers intact. This is the same unclamped-length class as DF-2522 (trm), which this project already reproduced with the production handler. Fixed-caller model (vpo.c clamps to sizeof sense_data before the call) A/B: 32 bytes written, markers intact.

Evidence (decisive lines)

DF2530[sense_len=255]: sizeof(sense_data)=32; verbatim transfer wrote count=255 bytes DF2530: sense bytes 28..39: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a DF2530: post-CCB: cdb_len=0x5a sglist_cnt=0x5a5a scsi_status=0x5a sense_resid=0x5a resid=0x5a5a5a5a (markers 11/22/33/44/55) DF2530: RESULT=OVERFLOW (sense_len>32 written into the rest of the CCB) --- control (sense_len=32) --- markers intact, clean --- fixed-caller model (clamp 32) --- markers intact, clean

Exploit chain / privesc assessment

local privileged (root/operator via pass(4) on the vpo bus, plus physical parallel-port device); bounded CCB corruption, not arbitrary write.

Fix + A/B

fix.diff clamps the REQUEST_SENSE length to sizeof(csio->sense_data) in vpo_intr before the call; A/B clean.

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

fixed
↓ fix.diffper-fix-DF-2530

Confirmed kernel references

Detail

Exploit chain

local privileged (root/operator via pass(4) on the vpo bus, plus physical parallel-port device); bounded CCB corruption, not arbitrary write.

Evidence (decisive lines)

run.log: 255 bytes written, markers destroyed; control 32/intact; fixed-caller model intact.

PoC changes

same transport rig as DF-2529; fixed-caller A/B models the clamped call the patched vpo.c makes.

Verified recommended fix

clamp the REQUEST_SENSE length to sizeof sense_data

Verdict

vpo_intr hands csio->sense_len (user CCB byte, 0..255 via pass(4) xpt_merge_ccb) and &csio->sense_data (32 bytes) to the transfer engine; the verbatim imm_do_scsi wrote 255 bytes (count=255) into the 32-byte field - the rig dump shows sense bytes 28..39 all 0x5A and the post-CCB markers destroyed: cdb_len=0x5a sglist_cnt=0x5a5a scsi_status=0x5a sense_resid=0x5a resid=0x5a5a5a5a (pre-set markers 11/22/33/44/55) - 223 bytes past the field into the rest of union ccb, exactly the field list in the finding. Control (sense_len=32) leaves the markers intact. This is the same unclamped-length class as DF-2522 (trm), which this project already reproduced with the production handler. Fixed-caller model (vpo.c clamps to sizeof sense_data before the call) A/B: 32 bytes written, markers intact.