β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2274

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2274 Β· 6 files
FileTypeDescriptionSize
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
VERDICT.md verdict gate analysis + source trace + exploit-chain stop reason
↓ download 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_testable
baseline no→ patch + rebuild →patched clean

not_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).
↓ fix.diffn/a (no target HW/device on this guest)

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.