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

CDRIOCSETBLOCKSIZE accepts block_size=0 -> divide-by-zero kernel panic

Summary

CDRIOCSETBLOCKSIZE stores user-supplied int directly into cdp->block_size with no validation then immediately calls acd_set_ioparm which computes rounddown(cdp->iomax cdp->block_size) macro divides by second argument. block_size=0 triggers integer divide-by-zero trap in kernel context. Any operator-group user single ioctl deterministic panic no disc required. O_RDONLY sufficient no FWRITE check on ioctl. [VERIFIED: deterministic privileged panic via ioctl on stock kernel; the operator-group unpriv arm REFUTED - raw disk open is SYSCAP_RESTRICTEDROOT-gated on current master]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2504 Β· 10 files
FileTypeDescriptionSize
poc_setblocksize.c β€” 2.0 KB view raw
build.sh β€” 145 B view raw
run.sh β€” 282 B view raw
run.log β€” 1.8 KB view raw
panic.txt β€” 761 B view raw
unpriv_open_test.log β€” 1.9 KB view raw
env.txt β€” 1.2 KB view raw
fix.diff β€” 348 B view raw
fix_run.log β€” 984 B view raw
verdict.json β€” 3.9 KB view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fixed

fix_run.log: patched kernel #1 (Thu Oct 15 23:37:29 UTC 2026) -- control 2352 ok; CDRIOCSETBLOCKSIZE(0) returns 'Invalid argument' (EINVAL), no trap, guest up. Baseline (kernel #0): same ioctl -> Fatal trap 18 at acd_set_ioparm+0x69 (panic.txt). 1/1 baseline panic vs 0/1 patched. fix.diff (EINVAL on <=0) applied to guest /usr/src only; audit sys/ tree untouched.
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Thu Oct 15 23:37:29 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

root/RESTRICTEDROOT: open(/dev/acd0, O_RDONLY) -> ioctl(CDRIOCSETBLOCKSIZE, 0) -> acd_ioctl atapi-cd.c:724 (no validation, no priv check) -> acd_set_ioparm :951 rounddown(65534, 0) -> fatal trap 18 -> kernel panic (DoS). No escalation: the primitive is a deterministic crash with fully controlled divisor sign/zero but no memory-safety impact.

Evidence (decisive lines)

panic.txt (Fatal trap 18 ... Stopped at acd_set_ioparm+0x69: divl %ecx,%eax from serial console); run.log (control 2352 ok + fatal run transcript); unpriv_open_test.log (operator-group EPERM at open on /dev/acd0, /dev/cd0, /dev/serno -- RESTRICTEDROOT gate); fix_run.log (patched kernel: EINVAL, survives).

PoC changes

Seed PoC sketch (none existed in the pack) rewritten as findings/poc/DF-2504/poc_setblocksize.c: correct CDRIOCSETBLOCKSIZE = _IOW('c',110,int) from sys/sys/cdrio.h, O_RDONLY open, control subcommand (2352 + CDRIOCGETBLOCKSIZE roundtrip) before the fatal 0 run.

Verified recommended fix

In acd_ioctl's CDRIOCSETBLOCKSIZE case reject non-positive (ideally non-2048/2352) block sizes with EINVAL before storing and calling acd_set_ioparm.

Verdict

Divide-by-zero CONFIRMED and reproduced on the real kernel via a single userland ioctl on the real /dev/acd0 (empty QEMU DVD-ROM, O_RDONLY, no disc): Fatal trap 18, Stopped at acd_set_ioparm+0x69: divl -- exactly rounddown(iomax,0) at atapi-cd.c:951 after :724 stores the unvalidated user int. Control roundtrip (2352) works; zero panics deterministically; single-fix kernel returns EINVAL and survives (fix validated). IMPORTANT THREAT-MODEL CORRECTION: the unprivileged-operator arm is REFUTED on current master -- opening /dev/acd0 (or /dev/cd0, or any raw disk device) requires SYSCAP_RESTRICTEDROOT (diskopen, sys/kern/subr_disk.c:1070-1073; EPERM demonstrated for operator-group user in unpriv_open_test.log), so the panic is only reachable by root/RESTRICTEDROOT principals. Effective severity: Low (privileged-user deterministic panic), not the filed Medium unpriv DoS.