ciss_cam_complete_fixup: padstr writes 16 bytes into the 4-byte inq->revision -> 12-byte heap overflow past short CAM INQUIRY buffers
Summary
padstr(inq->revision, ..., 16) writes 16 bytes at buffer offset 32, but revision is char[4] and the only gate is dxfer_len >= SHORT_INQUIRY_LENGTH (36) - a 36-byte CAM INQUIRY data buffer is overflowed by 12 bytes of status-string content. VERIFIED: the three padstr calls at exact struct offsets against a canary'd 36-byte buffer - 'write at buffer+36', 12 bytes past; -DFIXED (length 4) leaves the canary intact. Root/operator-gated via CAM pass-through (/dev/passN 0600); in-kernel periphs allocate the full struct and are safe. Upstream-inherited.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3730 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | faithful userspace replica of the cited kernel path (real macros/struct constants) | 4.0 KB | view raw |
| build.sh | build-script | cc -O2 (and -DFIXED replica) | 65 B | view raw |
| run.sh | run-script | baseline + fixed runs with exit codes | 89 B | view raw |
| build.log | build-log | full untrimmed guest compiler output | 43 B | view raw |
| run.log | run-log | decisive run: baseline [BUG REPRODUCED] + fixed [FIXED] | 953 B | view raw |
| run.2.log | run-log | repeat run (stability) | 953 B | view raw |
| run.3.log | run-log | repeat run (stability) | 953 B | view raw |
| fix_run.log | run-log | -DFIXED section (persist_fix reads this) | 373 B | view raw |
| VERDICT.md | verdict | full narrative: mechanism, path:line, harness proof, fix | 2.8 KB | β raw |
| fix.diff | suggested-fix | bound the revision padstr to sizeof(inq->revision) (4 bytes) | 438 B | view raw |
| fix_apply.log | apply-log | patch --dry-run on guest /usr/src (clean) | 2.8 KB | view raw |
| fix_build.log | build-log | ciss.ko rebuilt with all 7 pass-2 fixes at -Werror, link rc=0 | 1.0 KB | view raw |
| env.txt | environment | guest uname, no-HW proof, module presence | 538 B | view raw |
| README.md | readme | human reproduce doc | 1.2 KB | β raw |
| manifest.json | manifest | this file | 2.9 KB | view raw |
| verdict.json | verdict | machine verdict (persist_poc.py schema) | 2.0 KB | view raw |
DF-3730 β ciss_cam_complete_fixup: padstr writes 16 bytes into the 4-byte INQUIRY revision field
File: sys/dev/raid/ciss/ciss.c:3325
(padstr(inq->revision, ciss_name_ldrive_status(...), 16); β
struct scsi_inquiry_data.revision is char[4] at buffer offset 32,
scsi_all.h:738-739). Gate is only csio->dxfer_len >= SHORT_INQUIRY_LENGTH
(36) at ciss.c:3305 β 12 bytes past a 36-byte CAM data buffer.
Class: kernel heap OOB write (CWE-787), CAM-pass-through reachable (root/operator; /dev/passN is 0600 root:operator, scsi_pass.c:279-281). Upstream-inherited.
Reproduce
./build.sh && ./run.sh
Expected output
Baseline: write at buffer+36 (>= 36) β¦ [BUG REPRODUCED] 12 bytes written
past the end of the 36-byte INQUIRY data buffer. Fixed replica
(-DFIXED = sizeof(inq->revision)): canary intact, [FIXED].
Fix
fix.diff (bound the revision padstr to the field size). Compile-validated
with the other 6 ciss fixes at -Werror (fix_build.log); behaviorally
validated by the -DFIXED run.
Artifacts
VERDICT.md, harness.c, build.sh, run.sh, build.log, run.log,
run.2.log, run.3.log, env.txt, fix.diff, fix_apply.log,
fix_build.log, manifest.json, verdict.json.
DF-3730 VERDICT
Finding
After every completed INQUIRY against a logical volume,
ciss_cam_complete_fixup() rewrites the inquiry data to advertise the
volume's organisation and status:
/* sys/dev/raid/ciss/ciss.c:3323-3325 */
padstr(inq->vendor, "COMPAQ", 8); /* field is 8 β ok */
padstr(inq->product, ciss_name_ldrive_org(cl->cl_ldrive->fault_tolerance), 8); /* field is 16 β ok */
padstr(inq->revision, ciss_name_ldrive_status(cl->cl_lstatus->status), 16); /* field is 4! */
padstr (cissvar.h:374-384) writes exactly len bytes. The revision
field is char revision[SID_REVISION_SIZE=4] at buffer offset 32
(sys/bus/cam/scsi/scsi_all.h:738-739), so the third call writes bytes
32..47 β 12 bytes past the end of any inquiry buffer smaller than 48 bytes.
The only size gate on this path is csio->dxfer_len >= SHORT_INQUIRY_LENGTH
(36) at ciss.c:3305, so a standard 36-byte INQUIRY data buffer is smashed.
Reachability
Any CAM client issuing INQUIRY with a 36..44-byte data buffer against an
ONLINE logical volume. In-kernel periphs allocate
sizeof(struct scsi_inquiry_data) (β₯ 96) and are unaffected; the CAM
pass-through (/dev/passN, 0600 root:operator β scsi_pass.c:279-281)
allocates exactly the user-requested dxfer_len bytes, so a root/operator
camcontrol-style INQUIRY with a 36-byte buffer takes the 12-byte heap
overflow. Content is a NUL-padded fixed-set status string (limited
attacker control: e.g. "failed", "interim recovery", β¦). FreeBSD's driver
has the identical line (upstream-inherited).
Severity assessment: Low β requires CAM pass-through privileges (root / operator group), overflow is 12 bytes of semi-fixed string content past a kmalloc'd buffer.
Verification (no HP Smart Array on the audit guest)
harness.c replicates the three padstr calls at the exact struct offsets
with a canary'd 36-byte buffer:
- baseline (run.log, stable in run.2/run.3): write at buffer+36,
buffer[36..47] = 0x20 padding ("failed ..."), [BUG REPRODUCED]
12 bytes written past the end;
- -DFIXED (revision padstr bounded to 4): canary intact, [FIXED].
Fix validation
fix.diff:padstr(inq->revision, ..., sizeof(inq->revision));fix_apply.log: clean dry-run;fix_build.log:ciss.korebuilt with all 7 ciss fixes at-Werror, link rc=0;-DFIXEDharness run clean.
Bottom line: reproduced (harness-level); 12-byte heap overflow, root/operator -gated; fix validated at compile + replica level.
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
fixedrevision padstr bounded to sizeof(inq->revision) (4). -Werror module build + -DFIXED replica shows canary intact.
fix.diff; fix_apply.log; fix_build.log; fix_run.log [FIXED]
Confirmed kernel references
Detail
Exploit chain
CAM pass INQUIRY with 36-byte buffer on an ONLINE logical target -> completion fixup padstr(16) at offset 32 -> 12 bytes past kmalloc
Evidence (decisive lines)
run.log: write at buffer+36, [BUG REPRODUCED] 12 bytes past the 36-byte buffer; fix_run.log canary intact
PoC changes
harness written fresh; padstr instrumented with offset-checking stores
Verified recommended fix
bound the revision padstr to sizeof(inq->revision) (4 bytes)
Verdict
REPRODUCED (harness-level): ciss_cam_complete_fixup pads inq->revision (4-byte field, offset 32) with 16 bytes (ciss.c:3325) gated only by dxfer_len >= 36 (ciss.c:3305) -> 12-byte heap overflow past a 36-byte CAM INQUIRY buffer, reachable via CAM pass-through (/dev/passN 0600 root:operator). Severity Low (privileged reach, semi-fixed string content); upstream-inherited.
No comments yet.