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

twa: unchecked CAM cdb_len overflows the 16-byte firmware CDB field (heap/DMA-pool overflow)

Summary

csio->cdb_len (u8, up to 255) is copied unvalidated into the common-layer request; the CL memcpy's it into the fixed 16-byte cdb[16] of the firmware command packet - up to 239 fully attacker-controlled bytes past the field into the controller's contiguous DMA command-packet pool, corrupting the packet's own SGL and adjacent in-flight commands that the firmware then reads/writes. Root/operator via pass(4) CAMIOCOMMAND; in-kernel periphs never exceed 16 (hence Medium). No 3ware card on guest (trigger builds+runs to no-SIM). Validate-cdb_len<=16 fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4654 Β· 7 files
FileTypeDescriptionSize
twa_cdb.c β€” 3.0 KB view raw
run.log β€” 251 B view raw
env.txt β€” 271 B view raw
build.sh β€” 87 B view raw
run.sh β€” 94 B view raw
README.md β€” 1.6 KB ↓ raw
VERDICT.md β€” 2.0 KB ↓ raw

DF-4654 β€” twa CAM shim: unchecked cdb_len overflows 16-byte firmware CDB field

Files: sys/dev/raid/twa/tw_osl_cam.c:276-280 (feed) β†’ tw_cl_io.c:124 (sink) β†’ tw_cl_fwif.h:307-315 (struct tw_cl_command_9k: TW_UINT8 cdb[16]) Class: CWE-787/CWE-120 out-of-bounds write β€” Medium, certain (code), hardware-gated Verdict: NOT REPRODUCED on the audit guest (no 3ware 9000-series controller); defect proven by source trace + compiled trigger.

Trigger

twa_cdb.c: scans /dev/pass* via CAMIOCOMMAND XPT_PATH_INQ looking for a SIM named "twa"; when found, issues XPT_SCSI_IO with csio.cdb_len = 255 (CAM_CDB_POINTER, 0x41-filled). tw_osli_execute_scsi() copies cdb_len unchecked into scsi_req->cdb_len; tw_cl_start_io() then does tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len) into the 16-byte cdb[] of the firmware command packet β€” up to 239 fully attacker-controlled bytes past the field, into the contiguous bus_dmamem_alloc'd command-packet pool (tw_osl_freebsd.c:575): corrupts the packet's own SGL and the following request packets, which the controller then DMA-reads/writes (device-mediated memory corruption).

Guest result (run.log)

BUILD-OK; only SIM present is "ata" (QEMU DVD): "RESULT: no twa SIM present on this system" β€” the overflow path cannot execute without 3ware 9xxx hardware. Reachability on real iron: pass(4) CAMIOCOMMAND (root/operator) or any kernel CAM consumer issuing a >16-byte CDB.

Fix

Clamp in tw_osl_cam.c: reject csio->cdb_len > sizeof(cmd->cdb) (16) with CAM_REQ_INVALID before building scsi_req (see verdict.json fix).

VERDICT.md
↓ download raw

DF-4654 β€” twa CAM shim: unchecked cdb_len overflows 16-byte firmware CDB field

Files: sys/dev/raid/twa/tw_osl_cam.c:276-280 (feed) β†’ tw_cl_io.c:124 (sink) β†’ tw_cl_fwif.h:307-315 (struct tw_cl_command_9k: TW_UINT8 cdb[16]) Class: CWE-787/CWE-120 out-of-bounds write β€” Medium, certain (code), hardware-gated Verdict: NOT REPRODUCED on the audit guest (no 3ware 9000-series controller); defect proven by source trace + compiled trigger.

Trigger

twa_cdb.c: scans /dev/pass* via CAMIOCOMMAND XPT_PATH_INQ looking for a SIM named "twa"; when found, issues XPT_SCSI_IO with csio.cdb_len = 255 (CAM_CDB_POINTER, 0x41-filled). tw_osli_execute_scsi() copies cdb_len unchecked into scsi_req->cdb_len; tw_cl_start_io() then does tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len) into the 16-byte cdb[] of the firmware command packet β€” up to 239 fully attacker-controlled bytes past the field, into the contiguous bus_dmamem_alloc'd command-packet pool (tw_osl_freebsd.c:575): corrupts the packet's own SGL and the following request packets, which the controller then DMA-reads/writes (device-mediated memory corruption).

Guest result (run.log)

BUILD-OK; only SIM present is "ata" (QEMU DVD): "RESULT: no twa SIM present on this system" β€” the overflow path cannot execute without 3ware 9xxx hardware. Reachability on real iron: pass(4) CAMIOCOMMAND (root/operator) or any kernel CAM consumer issuing a >16-byte CDB.

Fix

Clamp in tw_osl_cam.c: reject csio->cdb_len > sizeof(cmd->cdb) (16) with CAM_REQ_INVALID before building scsi_req (see verdict.json fix).

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not validated (no hardware); fix is a 4-line bounds check in tw_osli_execute_scsi

per-fix-DF-4654

Confirmed kernel references

Detail

Exploit chain

operator/root -> camcontrol/CAMIOCOMMAND XPT_SCSI_IO with cdb_len>16 -> tw_osli_execute_scsi (no validation) -> tw_cl_start_io memcpy into cdb[16] -> SGL/packet corruption in DMA pool -> device-mediated kernel memory read/write

Evidence (decisive lines)

run.log (trigger build+run: 'no twa SIM present'); source trace tw_osl_cam.c:276-280 -> tw_cl_io.c:124 -> tw_cl_fwif.h:315

PoC changes

DFly specifics fixed vs seed: ccb_pathinq uses dev_name (not sim_name); CAMIOCOMMAND comes from scsi_pass.h; fixed comment nesting that broke the build

Verified recommended fix

In tw_osli_execute_scsi: if (csio->cdb_len > 16) { ccb_h->status = CAM_REQ_INVALID; xpt_done(ccb); return 1; }

Verdict

Code defect is certain: tw_osl_cam.c:280 copies csio->cdb_len (u_int8_t, up to 255, never validated) into scsi_req->cdb_len; tw_cl_io.c:124 memcpy's it into the 16-byte cdb[] field of struct tw_cl_command_9k (tw_cl_fwif.h:315), overflowing up to 239 controlled bytes into the contiguous DMA command-packet pool (tw_osl_freebsd.c:575) - corrupting this packet's SGL and adjacent packets that the firmware DMAs. Could not execute on the audit guest: trigger compiled and ran, but the only CAM SIM is 'ata' (QEMU DVD) - no 3ware 9000-series controller exists in QEMU. Reachability requires pass(4)/CAMIOCOMMAND (root/operator) on real hardware.