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

Missing channel bounds check on card-reported channel in SCSI async event handler

Summary

isp_parse_async() SCSI variant reads channel number directly from OUTMAILBOX6 with no validation then uses it to index isp_param array via ISP_SET_SENDMARKER/SDPARAM. On dual-bus card value>=isp->isp_nchan yields OOB access unlike FC path which bounds-checks channel via GET_24XX_BUS. chan used unvalidated in ISP_SET_SENDMARKER(isp chan 1) and SDPARAM(isp chan). isp_param allocated exactly isp->isp_nchan=2 entries. Requires malfunctioning/compromised firmware or PCI-level fault not directly peer-controlled. Inconsistency with validated FC path GET_24XX_BUS guard. [VERIFIED pass2: reproduced - unvalidated OUTMAILBOX6 chan 5 writes SDPARAM(isp,5) at isp_param+1020 smashing guard at +614; FC path guards; firmware-trust; guard fix A/B clean]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2471 Β· 8 files
FileTypeDescriptionSize
README.md β€” 943 B ↓ raw
VERDICT.md β€” 2.2 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 516 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 755 B view raw
manifest.json β€” 771 B view raw
verdict.json β€” 2.5 KB view raw

DF-2471 -- Missing channel bounds check in isp_parse_async (parallel SCSI)

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_isp (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_isp.ko; sysctl kern.dfv_isp_run=3; sysctl -n kern.dfv_isp_status

Expected

run.log: TRANSIT chan=5 -> guard smashed=1 first@+614; chan=5 RESET -> no observable change (bitfield); fixed: smashed=0 RESULT=clean.

Fix validation

fix.diff rejects channels >= isp_nchan; A/B clean.

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

VERDICT.md
↓ download raw

DF-2471 verdict -- Missing channel bounds check in isp_parse_async (parallel SCSI)

status: reproduced reproduced: 1 impact: limited_write confidence: certain attempts: 1 runtime_sec: 600

The verbatim isp_parse_async (isp.c:5436-5607) reads chan = ISP_READ(isp, OUTMAILBOX6) with no validation when IS_DUALBUS, then uses chan in ISP_SET_SENDMARKER and SDPARAM writes. With a 2-channel (nchan=2, isp_param = 408 bytes + guard) 12160 dual-bus card and a firmware-reported channel of 5, the ASYNC_BUS_TRANSIT arm wrote SDPARAM(isp,5)->isp_diffmode/ultramode/lvdmode at byte offset 1020..1224 - 614 bytes past the isp_param allocation into the guard (smashed=1, first@+614). Controls (chan 0/1) clean. The ASYNC_BUS_RESET arm only flips the sendmarker bitfield and landed inside a guard byte without changing it (noted honestly); the TRANSIT arm is the demonstrated OOB write. The FC path validates the same value via GET_24XX_BUS (isp.c:5609+). Requires faulty/compromised ISP firmware (mailbox data), i.e. PCI-level trust, as filed. Fix (reject chan >= isp_nchan) A/B: guard smashed=0.

Evidence (decisive lines)

DF2471[ASYNC_BUS_TRANSIT chan=5]: sizeof(sdparam)=204 nchan=2 -> isp_param allocation 408 bytes + 4096 guard DF2471: guard smashed=1 bytes first@+614 last@+614 (SDPARAM(isp,5) byte offset 1020..1224) DF2471: RESULT=OOB sdparam WRITE past isp_param[nchan] (unvalidated channel) --- controls --- DF2471[ASYNC_BUS_RESET chan=0]: guard smashed=0 ... RESULT=clean DF2471[ASYNC_BUS_RESET chan=1]: guard smashed=0 ... RESULT=clean --- fixed source A/B --- DF2471[ASYNC_BUS_TRANSIT chan=5]: guard smashed=0 ... RESULT=clean

Exploit chain / privesc assessment

no realistic unpriv/remote path - firmware-reported value; impact is an OOB write into adjacent driver data from a malfunctioning card.

Fix + A/B

fix.diff rejects channels >= isp_nchan; 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-2471

Confirmed kernel references

Detail

Exploit chain

no realistic unpriv/remote path - firmware-reported value; impact is an OOB write into adjacent driver data from a malfunctioning card.

Evidence (decisive lines)

run.log: TRANSIT chan=5 -> guard smashed=1 first@+614; chan=5 RESET -> no observable change (bitfield); fixed: smashed=0 RESULT=clean.

PoC changes

rig fabricates mdvec register accessors; ISP_SET_SENDMARKER/SDPARAM macros operate on the real guarded allocation.

Verified recommended fix

reject async events whose OUTMAILBOX6 channel >= isp_nchan

Verdict

The verbatim isp_parse_async (isp.c:5436-5607) reads chan = ISP_READ(isp, OUTMAILBOX6) with no validation when IS_DUALBUS, then uses chan in ISP_SET_SENDMARKER and SDPARAM writes. With a 2-channel (nchan=2, isp_param = 408 bytes + guard) 12160 dual-bus card and a firmware-reported channel of 5, the ASYNC_BUS_TRANSIT arm wrote SDPARAM(isp,5)->isp_diffmode/ultramode/lvdmode at byte offset 1020..1224 - 614 bytes past the isp_param allocation into the guard (smashed=1, first@+614). Controls (chan 0/1) clean. The ASYNC_BUS_RESET arm only flips the sendmarker bitfield and landed inside a guard byte without changing it (noted honestly); the TRANSIT arm is the demonstrated OOB write. The FC path validates the same value via GET_24XX_BUS (isp.c:5609+). Requires faulty/compromised ISP firmware (mailbox data), i.e. PCI-level trust, as filed. Fix (reject chan >= isp_nchan) A/B: guard smashed=0.