1-byte heap OOB read in OUT-data transfer path of imm_do_scsi
Summary
In data-transfer loop OUT-direction branch (device status 0x88 wants more data) selects transfer length of 2 whenever fewer than VP0_SECTOR_SIZE(512) bytes remain. Preceding guard at :739 only guarantees at least 1 byte remains. When exactly 1 byte left imm_outstr(vpo &buffer[*count] 2) reads 2 bytes - 1-byte OOB read of adjacent kernel heap. Leaked byte clocked out to parallel-port device. Sibling IN-direction branch (:751-755) correctly uses fallback of 1 confirming 2 is defect. OUT path reads in 2-byte microsequence pairs. Attacker: malicious parallel-port ZIP/Matchmaker device returns 0x88 status forcing OUT branch with odd dxfer_len (INQUIRY allocation length 255). Impact: 1 byte adjacent kernel heap leaked per CCB. Physical access required. [VERIFIED pass2: reproduced - OUT branch len 2 with 1 byte left clocks guard byte buffer 255 5a to the peripheral count 256; fallback-1 fix A/B zero OOB]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2529 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 882 B | β raw | |
| VERDICT.md | β | 1.7 KB | β raw | |
| build.sh | β | 99 B | view raw | |
| run.log | β | 359 B | view raw | |
| env.txt | β | 963 B | view raw | |
| fix.diff | β | 464 B | view raw | |
| manifest.json | β | 659 B | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-2529 -- 1-byte heap OOB read in imm_do_scsi OUT-data path
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=0; sysctl -n kern.dfv_vpo_status
Expected
run.log: OOB reads=1 byte=0x5a at buffer[255]; count=256; fixed 0.
Fix validation
fix.diff uses 1 as the short OUT fallback (matches the IN branch); A/B zero OOB.
Verdict: reproduced / impact=leak (see verdict.json, VERDICT.md)
DF-2529 verdict -- 1-byte heap OOB read in imm_do_scsi OUT-data path
status: reproduced reproduced: 1 impact: leak confidence: certain attempts: 2 runtime_sec: 700
The verbatim imm_do_scsi OUT branch picks a 2-byte transfer whenever fewer than VP0_SECTOR_SIZE bytes remain (immio.c:743-746); the :739 guard only guarantees 1 byte. With blen=255 and a scripted malicious ZIP+ (status 0x88 until the end), the loop ran to count=256 and the final imm_outstr(&buffer[254], 2) clocked out the guard byte at buffer[255] (0x5A) - one byte past the transfer buffer, captured as OUT[off=255]=5a OOB-READ. The sibling IN branch uses a fallback of 1, confirming the 2 is the defect. Fixed source (fallback 1) A/B: OOB reads=0, transfer completes.
Evidence (decisive lines)
DF2529: OUT transfer blen=255, buffer[255]=0x5A guard; count=256 ret=0 error=0 DF2529: OOB reads=1 byte=0x5a at buffer[255] (PAST the transfer buffer) DF2529: log: OUT[off=255]=5a OOB-READ DF2529: RESULT=1-BYTE HEAP OOB READ clocked to the peripheral (len=2 picked with exactly 1 byte remaining) --- fixed source A/B --- DF2529: OOB reads=0 ... RESULT=no OOB
Exploit chain / privesc assessment
malicious parallel-port peripheral (physical access); 1 adjacent kernel heap byte disclosed to the device per transfer.
Fix + A/B
fix.diff uses 1 as the short OUT fallback (matches the IN branch); A/B zero OOB.
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
fixedConfirmed kernel references
Detail
Exploit chain
malicious parallel-port peripheral (physical access); 1 adjacent kernel heap byte disclosed to the device per transfer.
Evidence (decisive lines)
run.log: OOB reads=1 byte=0x5a at buffer[255]; count=256; fixed 0.
PoC changes
scripted peripheral at the ppb transport boundary (stateful imm_wait); first scripted version answered the command phase with the data-phase status and was fixed.
Verified recommended fix
use 1 (not 2) as the short OUT transfer fallback
Verdict
The verbatim imm_do_scsi OUT branch picks a 2-byte transfer whenever fewer than VP0_SECTOR_SIZE bytes remain (immio.c:743-746); the :739 guard only guarantees 1 byte. With blen=255 and a scripted malicious ZIP+ (status 0x88 until the end), the loop ran to count=256 and the final imm_outstr(&buffer[254], 2) clocked out the guard byte at buffer[255] (0x5A) - one byte past the transfer buffer, captured as OUT[off=255]=5a OOB-READ. The sibling IN branch uses a fallback of 1, confirming the 2 is the defect. Fixed source (fallback 1) A/B: OOB reads=0, transfer completes.
No comments yet.