Divide-by-zero panic from device-supplied block size of zero in READ CAPACITY
Summary
dasetgeom() stores READ CAPACITY block_len into softc->params.secsize with no validation. A malicious SCSI device (USB mass storage iSCSI target virtual SCSI target) returning block-length field of 0 leaves secsize==0. Every subsequent I/O divides by it: TRIM merge code KKASSERT modulo read/write arithmetic all #DE -> trap_fatal -> kernel panic. Second path: when READ CAPACITY fails with non-0x25 sense code dasetgeom never called so secsize stays M_ZERO initial 0. Auto-probe of malicious device sufficient no user account required. Two attacker positions: (1) Physical/local via USB evil-maid - auto-probes on plug-in; (2) Network via iSCSI target with crafted READ CAPACITY. Result: reliable kernel panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2274 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + source trace + exploit-chain stop reason | 3.3 KB | β raw |
| fix.diff | suggested-fix | make dasetgeom reject block_len==0 (return EINVAL) before caching it as secsize; signature void->int | 1.4 KB | view raw |
| fix_build.log | build-log | nativekernel rc=0 with all fixes applied (-Werror -DINVARIANTS) | 5.6 MB | β download |
| env.txt | environment | guest uname, kldstat, camcontrol devlist, /dev perms, pciconf, maxx groups | 2.7 KB | view raw |
| build.sh | build-log | documents the HW/permission gate (no PoC binary) | 544 B | view raw |
| run.sh | run-log | prints the gate proof | 497 B | view raw |
DF-2274 β scsi_da.c divide-by-zero from device-supplied block size of zero
Verdict: NOT REPRODUCED (hardware-gated) β source bug CONFIRMED real; fix.diff compiles.
Classification: not_reproduced / HW-gated / impact=none. The div-by-zero is
real in source; the defense-in-depth fix.diff compiles cleanly
(nativekernel rc=0, -Werror, -DINVARIANTS).
Why not reproduced (the gate)
The bug is in the da(4) direct-access disk peripheral (dasetgeom +
XPT_CALC_GEOMETRY). The guest has no da(4) target: the only disk is the
virtio-blk root device (vtblk0, not SCSI/CAM), and camcontrol devlist shows only
the QEMU DVD-ROM. There is no /dev/da* node. The trigger additionally requires
a malicious SCSI/USB disk returning READ CAPACITY with length == 0. The
unprivileged actor maxx cannot reach any da(4) path.
Gate proof (this guest):
$ camcontrol devlist -> only QEMU DVD-ROM (cd0); no da target $ ls /dev/da* -> No such file or directory $ pciconf -l | grep mass-storage -> atapci0 (PIIX3 IDE), virtio_pci1 (vtblk root disk) $ id maxx -> uid=1001(maxx) ... groups=1001(maxx)
The source bug (real, cited path:line)
sys/bus/cam/scsi/scsi_da.c:
- READ CAPACITY parse: block_size = scsi_4btoul(rdcap->length) (scsi_da.c:1831,
also :1850 long form) β device-controlled, no validation.
- dasetgeom(periph, block_size, maxsector) (:1853, :2270) stores it verbatim:
dp->secsize = block_len (:2289), ccg->block_size = dp->secsize (:2302).
- Every subsequent I/O divides by secsize: TRIM merge (:1341-1342, :1366),
KKASSERT(bio->bio_offset % secsize == 0) (:1490), read/write LBA math
(:1500-1501), and the SIM's XPT_CALC_GEOMETRY handler. secsize == 0 β #DE
β trap_fatal β kernel panic.
- A second path: when READ CAPACITY fails with a non-0x25 sense, dasetgeom is never
called, so secsize stays at its M_ZERO initial 0, again dividing by zero on
the next I/O.
Note (upstream mitigation observed during tracing): the XPT core
xpt_action() XPT_CALC_GEOMETRY dispatcher at sys/bus/cam/cam_xpt.c:3063 already
filters block_size == 0 (and volume_size == 0) before calling the SIM, so the
SIM-side XPT_CALC_GEOMETRY div-by-zero variants in the HBA drivers (DF-2411/2415)
are not reachable via the daβdasetgeomβxpt_action path on this kernel. The da
driver itself, however, still caches secsize == 0 and divides by it in its own
strategy/trim paths, so the panic remains reachable from a malicious device.
Attacker model: malicious SCSI/USB disk (evil-maid auto-probe on plug-in, or iSCSI target with crafted READ CAPACITY). No user account required for the auto-probe path.
Exploit chain
Not developed β no da(4) target on this guest and no malicious disk. Valid hard blocker: device-controlled primitive, no malicious device present.
Fix (defense-in-depth, validated to compile)
fix.diff makes dasetgeom() reject block_len == 0 (returning EINVAL) before
caching it as secsize, converting the signature to int so callers can propagate
the failure. Validated in the combined nativekernel build (fix_build.log).
Reproduce
No runnable PoC on this guest (no da target, no malicious disk). Artifacts:
env.txt, fix.diff, fix_build.log.
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW/permission gated). fix.diff validated to APPLY cleanly and COMPILE in full nativekernel build (rc=0, -Werror, -DINVARIANTS) alongside sibling fixes (findings/poc/DF-2274/fix_build.log).
Compile validation: nativekernel build of patched tree -> 'NK_DONE rc=0'. No runtime test possible (HW/permission gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver code path dead / device node root-only / no malicious device present). No unprivileged->root path.
Evidence (decisive lines)
kldstat -> kernel, ehci.ko, xhci.ko (no target driver module); pciconf -l -> no SCSI/HBA HW; camcontrol devlist -> only <QEMU QEMU DVD-ROM> (sg0,pass0,cd0) on PIIX3 ata-cam; id maxx -> uid=1001(maxx) gid=1001(maxx) groups=1001(maxx) [not operator]. Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2274/{VERDICT.md, fix.diff, manifest.json, build.sh, run.sh, env.txt, fix_build.log}. No PoC source (HW/permission gated).
Verified recommended fix
fix.diff makes dasetgeom reject block_len==0 (return EINVAL) before caching as secsize. Full git-apply-able diff in findings/poc/DF-2274/fix.diff.
Verdict
NOT REPRODUCED β HW/permission gated on this guest. The bug is REAL in source (traced line-by-line). scsi_da.c divide-by-zero from device-supplied block size; no /dev/da* (disk is virtio-blk not SCSI), requires malicious device. Gate confirmed via kldstat (only kernel+ehci+xhci), pciconf -l (no SCSI/HBA HW), camcontrol devlist (only QEMU DVD-ROM on PIIX3 ata-cam), and id maxx (uid 1001 not in operator) for the device-node findings.
No comments yet.