OOB heap read in READ ELEMENT STATUS response parsing (device-controlled desclen/avail/flags)
Summary
chgetelemstatus at scsi_ch.c:1118 desclen=scsi_2btoul(pg_hdr->edl) device-controlled. :1159 avail=scsi_2btoul(st_hdr->count) device-controlled. copy_element_status :983-1051 reads ~84 bytes per desc unconditionally (desc->eaddr/flags1/sense_code/sense_qual/dt_scsi_flags/dt_scsi_addr + pvoltag[36] + avoltag[36]). desclen<84 -> reads (84-desclen) bytes past buffer. avail>cesr_element_count -> loop runs past data allocation. user_data sized for avail but data sized for cesr_count -> heap OOB read into user_data -> copyout to user. Root-only (SYSCAP_RESTRICTEDROOT chopen). Malicious SCSI changer device. Fix: clamp avail to cesr_count, check desclen>=sizeof(desc), validate resid.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1028 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc.c | trigger-source | documentation harness (no live trigger possible on guest) | 4.1 KB | view raw |
| build.sh | build-script | cc -O -pipe -Wall -o poc poc.c | 140 B | view raw |
| run.sh | run-script | ./poc | 63 B | view raw |
| build.log | build-log | harness compile output, full | 13 B | view raw |
| run.log | run-log | harness run output | 1.3 KB | view raw |
| fix.diff | suggested-fix | reject desclen<sizeof(descriptor), clamp avail to cesr_element_count | 1.3 KB | view raw |
| fix_build.log | build-log | cam.ko module compile with fix applied, full output (clean, -Werror) | 19.5 KB | view raw |
| env.txt | environment | uname, cc, kldstat, pciconf, modules | 1.7 KB | view raw |
| VERDICT.md | verdict | narrative analysis | 4.5 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1028 β VERDICT
Verdict
NOT REPRODUCED (live) β bug CONFIRMED via code trace, fix.diff compiles cleanly.
The OOB heap read in READ ELEMENT STATUS parsing is real and the
cited line numbers are exact, but it cannot be exercised on the audit
guest because the only SCSI device on the guest is a QEMU DVD-ROM (CD
-ROM, SCSI device type 5) and the ch peripheral only attaches to SCSI
device type 8 (medium changer). There is no /dev/ch0 and therefore
no code path to chgetelemstatus(). The bug is latent on this
guest and live on any host that has a (real or emulated) SCSI medium
changer against which a privileged user issues CHIOGSTATUS.
Mechanism (code trace)
chgetelemstatus() (scsi_ch.c:1053-1200) drives a SCSI READ
ELEMENT STATUS command and parses the response. Three device-controlled
quantities drive the parser with no validation:
-
sys/bus/cam/scsi/scsi_ch.c:1118c desclen = scsi_2btoul(pg_hdr->edl);The per-descriptor length, taken from the device's response page header. Attacker controls this. -
sys/bus/cam/scsi/scsi_ch.c:1159c avail = scsi_2btoul(st_hdr->count);The number of available elements, taken from the device's response status header. Attacker controls this. -
The loop at
scsi_ch.c:1176-1183c for (i = 0; i < avail; ++i) { ... copy_element_status(softc, pg_hdr->flags, desc, ces); desc = (struct read_element_status_descriptor *) ((uintptr_t)desc + desclen); }iteratesavailtimes and reads ~84 fixed bytes per descriptor regardless ofdesclen.
copy_element_status() (scsi_ch.c:983-1051) unconditionally
dereferences desc->eaddr, flags1, reserved0, sense_code,
sense_qual, dt_scsi_flags, dt_scsi_addr, reserved1, flags2,
ssea, pvoltag (36 bytes), avoltag (36 bytes) β about 84 bytes
total (verified from struct read_element_status_descriptor at
scsi_ch.h:208-252). Two distinct OOB reads follow:
-
desclen < 84: each call to
copy_element_statusreads(84 - desclen)bytes past the per-descriptor slot.datais sized fordesclen * cesr->cesr_element_countbytes, so even one short descriptor overruns the end ofdata. -
avail > cesr->cesr_element_count: the loop walks
descpast the data allocation, copying the OOB-read bytes intouser_data(which is sized foravail, so no overflow there). The leaked kernel heap bytes are thencopyoutto the (privileged) user viascsi_ch.c:1186-1188.
The ch driver is part of the cam.ko module, which IS loaded on the
audit guest (the QEMU DVD-ROM uses CAM via natapicam). But the ch
peripheral only attaches when CAM detects a SCSI device type 8.
Why it does not reproduce on this guest
| Audit-guest fact | Evidence |
|---|---|
| Only SCSI device is a QEMU DVD-ROM (type 5, CD-ROM) | camcontrol devlist shows only QEMU DVD-ROM at scbus1 target 0 lun 0 |
No /dev/ch0 device node exists |
ls /dev/ch* returns nothing |
| ch peripheral never registers because no medium-changer device is enumerated | ch only matches T_CHANGER (type 8) at peripheral registration |
There is no code path on the audit guest that reaches
chgetelemstatus().
Exploit chain
none β pure OOB heap read (CWE-125). The ch driver's chopen is
gated by SYSCAP_RESTRICTEDROOT so this is a privileged-user-only
read; on a host with a malicious SCSI changer the leak ceiling is
"kernel heap bytes the size of the (avail-count) gap past data,
re-routed to userspace". Not exploitable as corruption.
Fix
fix.diff:
1. Rejects desclen < sizeof(struct read_element_status_descriptor)
(84 bytes) with EIO and a diagnostic print, preventing the
per-slot OOB read.
2. Clamps avail to cesr->cesr_element_count, preventing the loop
from walking past data.
Both checks land before the user_data kmalloc and the descriptor
loop, so they close both OOB paths.
Fix validation
fix.diffapplies cleanly to/usr/src/sys/bus/cam/scsi/scsi_ch.cwithpatch -p1(1 hunk succeeded).- The patched
cam.komodule (which containsscsi_ch.o) compiles cleanly with-Werror(seefix_build.log). - Not live-tested (no SCSI changer device to attach).
fix_status: not_testable (no live trigger available).
PoC changes
The finding folder was empty; this run authored:
- poc.c β documentation harness
- fix.diff β the verified fix
- build.sh, run.sh, build.log, run.log, env.txt,
fix_build.log, manifest.json, VERDICT.md
Fix verification
not_testablecompile validated
module/kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. chgetelemstatus desclen/avail no validation -> OOB read. No SCSI changer. Fix compiles.
No comments yet.