OOB heap read via unchecked blk_desc_len in MODE SENSE parsing (chdone/chgetparams)
Summary
find_mode_page_6 at scsi_all.h:1418 page_start=&mode_header[1]+mode_header->blk_desc_len. blk_desc_len u_int8_t offset 3 fully attacker-controlled. chdone :546-547 ea=find_mode_page_6(mode_header); reads 16 bytes from ea. chgetparams :1409-1419 reads ea fields + :1474-1484 reads cap fields. mode_buffer 32 bytes; blk_desc_len=0xFF -> page pointer mode_buffer+259 -> ~240+ bytes OOB kernel heap read into softc sc_firsts/sc_counts (exposed via CHIOGPARAMS) or panic on page boundary. Auto-triggered on device probe (chstart->chdone). Fix: validate blk_desc_len against buffer size before find_mode_page_6.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1029 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc.c | trigger-source | documentation harness (no live trigger possible on guest) | 3.3 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 | add ch_mode_header_sane() helper, validate before each find_mode_page_6 call | 2.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.3 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-1029 β VERDICT
Verdict
NOT REPRODUCED (live) β bug CONFIRMED via code trace, fix.diff compiles cleanly.
The OOB heap read via unchecked blk_desc_len is real and the cited
line numbers are exact, but it cannot be exercised on the audit guest
for the same reason as DF-1028: there is no SCSI medium-changer
device, so no ch peripheral attaches and the vulnerable code paths
(chstart β chdone, and chgetparams) never run. The bug is
latent on this guest and live on any host that attaches a SCSI
changer; unlike DF-1028, the trigger is automatic at probe time β
no userland cooperation is required.
Mechanism (code trace)
find_mode_page_6() at sys/bus/cam/scsi/scsi_all.h:1418-1427:
static __inline void *
find_mode_page_6(struct scsi_mode_header_6 *mode_header)
{
void *page_start;
page_start = (void *)((u_int8_t *)&mode_header[1] +
mode_header->blk_desc_len);
return(page_start);
}
blk_desc_len is a u_int8_t at offset 3 of
struct scsi_mode_header_6 (scsi_all.h:956-962) β i.e. a byte the
device fully controls in its MODE SENSE response. The function adds
that byte to &mode_header[1] (= mode_header + 4) with NO bounds
check, so a blk_desc_len == 0xFF (255) makes page_start point 259
bytes into a buffer that may be far smaller.
chstart at sys/bus/cam/scsi/scsi_ch.c:485-519 allocates the probe
mode buffer with:
mode_buffer_len = sizeof(struct scsi_mode_header_6) + // 4
sizeof(struct scsi_mode_blk_desc) + // 8
sizeof(struct page_element_address_assignment); // 20
// total = 32 bytes
If the device lies and returns blk_desc_len == 0xFF,
find_mode_page_6(mode_header) returns mode_buffer + 4 + 255 =
mode_buffer + 259 β 227 bytes past the 32-byte allocation.
Three call sites then dereference that OOB pointer:
sys/bus/cam/scsi/scsi_ch.c:546-547(probe path,chdone): readsea->mtea/nmte/fsea/nse/fieea/niee/fdtea/ndte(16 bytes) intosoftc->sc_firsts[]/sc_counts[].sys/bus/cam/scsi/scsi_ch.c:1409-1419(chgetparams, element page): same 16-byte read ofea->intosoftc->sc_firsts[]/sc_counts[].sys/bus/cam/scsi/scsi_ch.c:1474-1484(chgetparams, cap page): readscap->move_from[]/exchange_with[](16 bytes) intosoftc->sc_movemask[]/sc_exchangemask[].
Some of the resulting softc fields are then exposed to userspace via
CHIOGPARAMS (slot counts). This is an automatic OOB read at
device-probe time β no userland action needed beyond attaching the
device.
Why it does not reproduce on this guest
Same as DF-1028: no SCSI device type 8 (medium changer) is present,
so no ch peripheral attaches and chstart/chdone/chgetparams
never run. The cam.ko module is loaded (QEMU DVD-ROM uses CAM), but
the ch driver only matches T_CHANGER.
Exploit chain
none β pure OOB heap read (CWE-125) at probe time. Read size is
up to ~16 bytes per call site, fed into softc state that is partially
readable by CHIOGPARAMS (root-only). On a malicious-changer
scenario, the read ceiling is bounded by the slab bucket / page
adjacency of the 32-byte probe mode_buffer.
Fix
fix.diff adds a small static __inline int ch_mode_header_sane()
helper that requires mode_header->blk_desc_len <= buflen -
sizeof(*mode_header), and calls it at all three call sites before
find_mode_page_6(). On a bogus value the driver logs a diagnostic
and bails (probe announcement becomes empty; chgetparams returns
EIO). This is a minimal, root-cause fix that matches the finding's
"validate blk_desc_len against buffer size before find_mode_page_6"
recommendation.
Fix validation
fix.diffapplies cleanly to/usr/src/sys/bus/cam/scsi/scsi_ch.cwithpatch -p1(4 hunks all succeeded; 2 with a 23-line offset because the helper was inserted above the first call site β patch auto-applied the offset).- 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. find_mode_page_6 blk_desc_len no bounds -> OOB read at probe. No SCSI changer. Fix compiles.
No comments yet.