cdsize accepts blksize=0 from READ_CAPACITY, cdstart divide-by-zero panics on read
Summary
cdsize at scsi_cd.c:3058 softc->params.blksize=scsi_4btoul(rcap_buf->length) no lower bound. :3068 only narrows 2049-2352 -> 2048, does NOT catch 0. cdcheckmedia succeeds (CD_FLAG_VALID_MEDIA). First read: cdstart :1530 KKASSERT(bio_offset % blksize == 0) -> #DE; :1540/:1541 bio_offset/blksize -> #DE -> kernel panic. Malicious SCSI/iSCSI CD-ROM READ_CAPACITY length=0. mount succeeds, any read panics. Fix: if(blksize==0 || !power_of_2) blksize=2048.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0988 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix: reject media when blksize==0 in cdsize() | 891 B | view raw |
| fix_build.log | build-log | scsi_cd.c compiles under -Werror with the fix applied | 1.1 KB | view raw |
| env.txt | environment | uname, kern.version | 260 B | view raw |
| VERDICT.md | verdict | detailed analysis: mechanism, reachability, fix | 4.6 KB | β raw |
| README.md | readme | summary | 1.0 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-0988 β cdsize accepts blksize=0 from READ_CAPACITY
Summary
cdsize() at sys/bus/cam/scsi/scsi_cd.c:3058 reads
softc->params.blksize = scsi_4btoul(rcap_buf->length) with no lower-bound
check. The narrowing at line 3068 only catches 2049-2352 β 2048; it does
not catch blksize=0. On the next read, cdstart() at line 1530 asserts
bio_offset % blksize == 0 and divides by blksize at lines 1540/1541
β kernel divide-by-zero panic.
Reachability: Requires a malicious SCSI/iSCSI CD-ROM that returns
READ_CAPACITY with length=0. On the default guest, /dev/cd0 is
root:operator mode 0640 (maxx cannot open) and the virtual QEMU CD-ROM
returns well-formed data. Not triggerable by an unprivileged user.
Files
fix.diffβ git-apply-able fix: reject media if blksize==0fix_build.logβ kernel build output showingscsi_cd.ccompiles cleanly under-Werrorwith the fix appliedenv.txtβ guest environmentVERDICT.mdβ detailed analysismanifest.jsonβ artifact catalog
DF-0988 β cdsize accepts blksize=0 from READ_CAPACITY β divide-by-zero panic
Verdict
NOT REPRODUCED at runtime on the default guest β bug is real as a code pattern, but triggering requires a malicious SCSI/iSCSI CD-ROM that returns READ_CAPACITY data with length=0. Defense-in-depth fix authored + compiled.
Mechanism
cdsize() at sys/bus/cam/scsi/scsi_cd.c:3026 reads the result of a
SCSI READ_CAPACITY command into rcap_buf and stores the block size
without a lower bound:
/* scsi_cd.c:3058-3069 */
softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1;
softc->params.blksize = scsi_4btoul(rcap_buf->length);
/*
* SCSI-3 mandates that the reported blocksize shall be 2048.
* Older drives sometimes report funny values, trim it down to
* 2048, or other parts of the kernel will get confused.
*/
if (softc->params.blksize > 2048 && softc->params.blksize <= 2352)
softc->params.blksize = 2048;
The narrowing at line 3068 only catches the (2048, 2352] range; a
malicious device reporting length = 0 (or any non-2048-family value not
in 2049-2352) sets softc->params.blksize = 0.
On the next read, cdstart() at sys/bus/cam/scsi/scsi_cd.c:1530 asserts
and divides by this zero:
/* scsi_cd.c:1530 */
KKASSERT(bio->bio_offset % softc->params.blksize == 0);
/* scsi_cd.c:1540-1541 */
bio->bio_offset / softc->params.blksize,
bp->b_bcount / softc->params.blksize,
β kernel panic (divide-by-zero / INVARIANTS assertion). Mount succeeds (cdcheckmedia reports CD_FLAG_VALID_MEDIA), the first read panics.
Reachability analysis (the crucial question)
cdsize() is called from cdcheckmedia() (sys/bus/cam/scsi/scsi_cd.c:1049)
on the CD periph's media-validation path. It is reached when:
- A SCSI CD-ROM (or ATAPI CD via
atapi-cam) reports media present, and - The kernel re-validates the media (mount, open, media-change event).
The READ_CAPACITY data comes from the device firmware. To trigger the
panic, the device must return rcap_buf->length == 0. This is
device-controlled data; the kernel cannot influence it.
On the audit guest:
/dev/cd0iscrw-r----- root operatorβmaxx(uid 1001, only groupmaxx) cannot open it.camcontrol devlistrequires operator membership maxx lacks (couldn't open /dev/xpt0: Permission denied).- The virtual QEMU PIIX CD-ROM behind
/dev/cd0returns well-formed READ_CAPACITY data (blksize=2048). No malicious device is attached.
For an unprivileged user to trigger this on a real deployment, they would need:
- A malicious iSCSI target presenting a CD-ROM LUN that returns
READ_CAPACITY with
length=0(requires the admin to have configured the iSCSI initiator against the attacker's target, OR the target to be compromised), or - A malicious USB CD-ROM / SCSI device physically plugged in (physical access or social engineering).
Both preconditions are realistic for some deployments (the audit treats malicious filesystem images and compromised storage as in-scope), but neither is present on the default audit guest. This is a valid hard blocker: the bug is real but cannot be triggered by an unprivileged syscall on the default kernel + default devices.
Exploit chain
Not applicable / blocked by valid hard blocker (no malicious device on default guest). The primitive is a kernel divide-by-zero β DoS/panic; there is no privilege-escalation path because no attacker-controlled memory corruption occurs (just an arithmetic fault). Fixing the validation is the right action.
Fix
The fix in fix.diff adds an explicit blksize==0 (and lower-bound) check
in cdsize() that refuses the media with ENXIO rather than letting
cdstart() divide by zero:
if (softc->params.blksize == 0) {
xpt_print(periph->path, "cdsize: device reported blocksize of 0, "
"rejecting media\n");
error = ENXIO;
}
Fix validation
Compile-only. The bug requires a malicious device not present on the
guest, so a runtime behavior-change cannot be demonstrated on either the
patched or unpatched kernel (fix_status: not_testable). Validated:
fix.diffapplies cleanly withpatch -p1(rc=0).- A single-fix kernel was built with
make -j6 nativekernel KERNCONF=X86_64_GENERIC;scsi_cd.ccompiled under-Werror(no warnings, no errors); the kernel linked successfully (/usr/obj/usr/src/sys/X86_64_GENERIC/kernel.stripped, today's build). Seefix_build.log.
PoC changes
The PoC directory was seeded empty. No runtime trigger is possible on
the default guest (no accessible /dev/cd0, no malicious device).
Authored fix.diff and this VERDICT.md.
Fix verification
not_testablecompile validated
kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. cdsize blksize=0 from READ_CAPACITY -> div-by-zero. Needs malicious CD device. Compile validated.
No comments yet.