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

Missing cdb_len validation in DISK I/O path allows OOB read on short CDB pointer

Summary

ahci_xpt_scsi_disk_io() computes CDB pointer from csio->cdb_io.cdb_ptr when CAM_CDB_POINTER set and accesses CDB structures up to 16 bytes wide (cdb->read_capacity_16 cdb->ata_pass_12 cdb->ata_pass_16 cdb->rw_10/12/16) without ever consulting csio->cdb_len. ATAPI sibling path correctly bounds this with cdb_len<6||cdb_len>16 check but DISK path does not. User-supplied CCB via /dev/pass with CAM_CDB_POINTER and cdb_len<16 causes kernel to read past user-supplied CDB buffer. SMAP faults kernel panic. With kernel-address cdb_ptr (requires KASLR bypass) 14 bytes kernel memory read into FIS fields. [VERIFIED pass2: reproduced on live QEMU AHCI - cdb_len 6 CDB at page end with PROT_NONE neighbor Fatal trap user address access at ahci_xpt_scsi_disk_io+0x8f0 reading cdb+7; operator-group pass reach; ATAPI-style check fix-validated]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2422 Β· 11 files
FileTypeDescriptionSize
README.md β€” 344 B ↓ raw
VERDICT.md β€” 1.3 KB ↓ raw
build.sh β€” 89 B view raw
env.txt β€” 254 B view raw
fix.diff β€” 528 B view raw
manifest.json β€” 723 B view raw
panic.txt β€” 494 B view raw
pass2422.c β€” 2.2 KB view raw
run.log β€” 406 B view raw
run.sh β€” 148 B view raw
verdict.json β€” 1.9 KB view raw

DF-2422 -- evidence pack

Verdict: reproduced (reproduced=1, impact=panic, confidence=certain)

Build: cc -O -o pass2422 pass2422.c -lcam Run: ./pass2422 -c (control: completes) ; ./pass2422 (trigger: panic)

See VERDICT.md for the full narrative, run.log/panic.txt for the decisive output, fix.diff for the post-verification fix.

VERDICT.md
↓ download raw

DF-2422 VERDICT

Status: reproduced Impact: panic Confidence: certain

ahci_xpt_scsi_disk_io never consults csio->cdb_len: a pass(4) XPT_SCSI_IO CCB with CAM_CDB_POINTER and cdb_len=6 whose CDB buffer ends at a page boundary (next page PROT_NONE) made the kernel read cdb->rw_10 bytes 6..9 past the user buffer: Fatal user address access from kernel mode, fault VA = cdb_page+1, stopped at ahci_xpt_scsi_disk_io+0x8f0 movzwl 0x7(%r13),%ecx - exactly the 16-bit block-count field at cdb+7. The control run (identical CDB, pointer mid-page) completed normally, proving the DISK path is reached and the fault is the OOB read.

Kernel references

Guest

DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Notes

the ATAPI sibling path already validates cdb_len (ahci_cam.c:1512); the fix mirrors it

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_testable
↓ fix.diffper-fix-DF-2422

Confirmed kernel references

Detail

Exploit chain

operator-group /dev/passN + a crafted CCB -> kernel reads up to 10 CDB bytes regardless of cdb_len -> with an unmapped neighbor page: fatal kernel-mode fault (local DoS); with kernel-address cdb_ptr (KASLR bypass): 14 bytes of kernel memory read into FIS fields

Evidence (decisive lines)

run.log (control output), panic.txt (fault VA and stopped-at instruction)

PoC changes

live QEMU AHCI hardware (ahci0.0 DISK, /dev/pass0); trigger via libcam CAM_CDB_POINTER CCB; no rig needed

Verified recommended fix

mirror the ATAPI check in ahci_xpt_scsi_disk_io: reject CCBs with csio->cdb_len < 6 || > 16 before dereferencing the CDB

Verdict

ahci_xpt_scsi_disk_io never consults csio->cdb_len: a pass(4) XPT_SCSI_IO CCB with CAM_CDB_POINTER and cdb_len=6 whose CDB buffer ends at a page boundary (next page PROT_NONE) made the kernel read cdb->rw_10 bytes 6..9 past the user buffer: Fatal user address access from kernel mode, fault VA = cdb_page+1, stopped at ahci_xpt_scsi_disk_io+0x8f0 movzwl 0x7(%r13),%ecx - exactly the 16-bit block-count field at cdb+7. The control run (identical CDB, pointer mid-page) completed normally, proving the DISK path is reached and the fault is the OOB read.