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

mly_cam_complete() INQUIRY rewrite writes vendor/product/revision at data_ptr[8..36) regardless of dxfer_len β€” heap OOB write via short pass(4) INQUIRY CCB on a logical device

Summary

On SCSI_STATUS_OK with CDB[0]==INQUIRY for a logical target, padstr() unconditionally rewrites inquiry fields at fixed offsets 8/16/32 of csio->data_ptr; pass(4) lets the caller pick any dxfer_len, so buffers shorter than 36 bytes are overrun by up to ~32 bytes (content static driver text, length/offset attacker-chosen - adjacent slab chunk-header corruption). Distinct from DF-1289 (the sense path). Gate-on-dxfer_len fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3930 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.0 KB ↓ raw
VERDICT.md β€” 2.8 KB ↓ raw
fix.diff β€” 905 B view raw
env.txt β€” 543 B view raw
verdict.json β€” 2.4 KB view raw

DF-3930 β€” mly: mly_cam_complete() INQUIRY rewrite writes past a short CAM data buffer (heap OOB write)

Build / Run (on hardware)

Needs an mly controller with a logical device and access to its pass(4) device (root, or operator where devfs grants it):

camcontrol cmd 3:0:0 -c "12 00 00 00 04 00" -d 4 # 4-byte INQUIRY data-in

or equivalently a pass(4) XPT_SCSI_IO CCB with cdb_len=6, cdb[0]=INQUIRY (0x12), dxfer_len=4, CAM_DIR_IN targeting the logical volume. sc->mly_btl[bus][target].mb_flags & MLY_BTL_LOGICAL must be set (it is, for mly virtual targets).

Expected result

mly_cam_complete() (status OK, cmd==INQUIRY) unconditionally rewrites vendor/product/revision at data_ptr+8..+36 (padstr calls, mly.c:2343-2345) regardless of dxfer_len. With a 4-byte buffer this writes ~32 bytes of device-descriptive text past a 4-byte kernel allocation β€” adjacent heap corruption (mchunk metadata / neighbouring object headers).

No mly hardware in the audit guest β†’ not runtime-reproduced; source-certain. See VERDICT.md.

VERDICT.md
↓ download raw

DF-3930 verdict narrative

Reproduced?

No (hardware-gated: guest has no mly controller). Source-level certainty: certain.

Root cause chain (path:line)

mly.c:2331-2346 (mly_cam_complete, SCSI_STATUS_OK path): c if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_LOGICAL) { cmd = <cdb[0]>; /* 0x12 == INQUIRY */ if (cmd == INQUIRY) { btl = &sc->mly_btl[bus][target]; inq = (struct scsi_inquiry_data *)csio->data_ptr; /* :2318 */ padstr(inq->vendor, ..., 8); /* writes data_ptr[8..16) */ padstr(inq->product, ..., 16); /* writes data_ptr[16..32) */ padstr(inq->revision, "MYLX", 4); /* writes data_ptr[32..36) */ } }

csio->data_ptr / dxfer_len come straight from the CAM client. For a normal probe INQUIRY dxfer_len is 36 so the rewrite fits. But pass(4) forwards user-chosen CCBs: a 6-byte INQUIRY CDB with a short data-in buffer (e.g. 4 bytes β€” the allocation pass makes for the transfer) reaches the driver verbatim; the controller truncates its reply, status comes back OK, and padstr then writes at offsets 8..36 of a 4-byte kmalloc buffer β€” an out-of-bounds heap write of up to 32 bytes past the allocation.

Layout facts: struct scsi_inquiry_data has vendor at offset 8, product at 16, revision at 32 (scsi_all.h); padstr (mlyvar.h:362-372) writes exactly len bytes.

The written content is static text (mly_tables.h strings, "MYLX") and space-fill β€” not attacker bytes, but the length/position are attacker-chosen (dxfer_len 0..35 selects how far past the end writes begin), and the clobbered neighbours are kernel heap metadata/objects. On this guest's slab allocator (a 4-byte request rounds to an 8-byte chunk) the write runs over the following chunk's header.

Threat model

Local privileged-ish user (pass device; typically root, operator where granted) on a machine with an mly logical volume. Requires the volume to report OK status for the short inquiry β€” it does, the card merely truncates.

Impact

Bounded (~32B) kernel heap OOB write with fixed content at a length-controlled offset β€” heap metadata corruption, panic most likely; exploitability limited by the static content but nonzero (chunk-header clobbering).

Distinct from knowns: DF-1289 is the sense_data overflow in the same function (mly.c:2356-2358); this is the INQUIRY-rewrite path (mly.c:2341-2346).

Fix

Gate the rewrite on csio->dxfer_len >= 36 β€” fix.diff.

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
baseline no→ patch + rebuild →patched clean

Fix not validated on the guest: the trigger path requires mly/age hardware absent from the audit guest. fix.diff is mechanical (bounds/termination-only) and authored post-verification against the cited lines.

findings/poc/DF-3930/fix.diff + VERDICT.md
↓ fix.diffper-fix-DF-3930

Confirmed kernel references

Detail

Exploit chain

privileged pass(4) user sends short INQUIRY data-in CCB to an mly logical volume -> padstr writes at data_ptr[8..36) past the short kernel buffer -> adjacent heap metadata corruption

Evidence (decisive lines)

findings/poc/DF-3930/VERDICT.md; README.md (exact camcontrol trigger); fix.diff (dxfer_len >= 36 gate)

PoC changes

No seed PoC; trigger documented as camcontrol/pass CCB one-liner in README.md.

Verified recommended fix

Only rewrite inquiry vendor/product/revision when csio->dxfer_len >= 36.

Verdict

Hardware-gated (no mly controller / logical volume in the guest); source-certain. mly_cam_complete (mly.c:2331-2346), on SCSI_STATUS_OK for a logical target with CDB[0]==INQUIRY, unconditionally rewrites the inquiry data at data_ptr+8..36 via padstr regardless of the CCB's dxfer_len. pass(4) forwards user-chosen buffers: a 6-byte INQUIRY CDB with a 4-byte data-in buffer (camcontrol cmd 3:0:0 -c '12 00 00 00 04 00' -d 4) makes the driver write ~32 bytes of static device-descriptive text past a 4-byte kmalloc - bounded heap OOB write with length/offset attacker-chosen (dxfer_len 0..35), clobbering adjacent slab chunk headers. Known DF-1289 covers the sense_data bcopy in the same function (:2356-2358); this is the distinct INQUIRY-rewrite path. Not runtime-reproduced (hardware absent); certain at source level.