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

Integer divide-by-zero panic in fwdma_malloc_multiseg via esize==0 (FW_SSTBUF psize=0)

Summary

fwdma_malloc_multiseg at fwdma.c:159 does ssize=rounddown(PAGE_SIZE,esize) which expands to (PAGE_SIZE/esize)*esize (param.h:400). With esize==0 this is integer divide by zero -> kernel #DE trap -> panic at trap.c:1101. esize is b->psize passed from fwdev_allocbuf (fwdev.c:108-109) after roundup2(b->psize,4) which is identity for 0. psize=0 set by user via FW_SSTBUF ioctl (fwdev.c:504-506 unchecked bcopy). Triggered by FW_STSTREAM/SRSTREAM -> fwdev_allocbuf -> fwdma_malloc_multiseg(esize=0). /dev/fwN.M created 0660 root:operator (fwdev.c:173-175) with NO priv_check on ioctl path. Attacker: any local user in group operator. Single-threaded, 100% reproducible, deterministic kernel panic. Fix: reject esize<=0||n<=0 at top of fwdma_malloc_multiseg.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1116 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source userspace div-by-zero primitive (volatile prevents constant-fold) 3.1 KB view raw
fix.diff suggested-fix reject esize<=0||n<=0 in fwdma_malloc_multiseg + EINVAL in fwdev_allocbuf 1.0 KB view raw
build.sh build-script cc -O2 -o harness harness.c 95 B view raw
run.sh run-script timeout 10 ./harness 67 B view raw
build.log build-log final successful build 13 B view raw
run.log run-log decisive run incl SIGFPE marker 295 B view raw
env.txt environment uname, cc version, pciconf (no FW HW) 403 B view raw
VERDICT.md verdict full narrative: mechanism + impact ceiling + fix 2.9 KB ↓ raw
README.md readme finding summary + build/run/expected 1.6 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
README.md readme finding summary + build/run/expected
↓ download raw

DF-1116 β€” Integer divide-by-zero in fwdma_malloc_multiseg (FireWire)

Finding

fwdma_malloc_multiseg at sys/bus/firewire/fwdma.c:159 computes ssize = rounddown(PAGE_SIZE, esize) which expands to (PAGE_SIZE / esize) * esize (sys/sys/param.h:400). With esize == 0 this is integer divide-by-zero β†’ kernel #DE trap β†’ panic at sys/platform/pc64/x86_64/trap.c:1101.

esize is b->psize passed from fwdev_allocbuf (sys/bus/firewire/fwdev.c:108) after roundup2(b->psize, 4) which is identity for 0 (fwdev.c:107). psize = 0 is user-supplied via the unchecked FW_SSTBUF ioctl (fwdev.c:504-506 bcopy(ibufreq, &d->bufreq, ...)).

Trigger path: FW_STSTREAM / FW_SRSTREAM β†’ fwdev_allocbuf β†’ fwdma_malloc_multiseg(esize=0) β†’ #DE.

Reachability on this guest

NOT reachable. The FireWire driver (firewire.ko) is loadable but no FireWire controller is present in the QEMU guest (pciconf -lv shows no 1394 OHCI device), so the driver never attaches and /dev/fwN.M is never created. The bug is real and deterministic but latent on this host.

A userspace harness reproduces the arithmetic primitive (SIGFPE = the user-mode analogue of kernel #DE).

Build / Run / Expected

cc -O2 -o harness harness.c     # build.sh
./harness                        # run.sh
# Expected: "SIGFPE: integer divide by zero -- primitive reproduced."

Files

  • harness.c β€” standalone reproduction of the div-by-zero arithmetic.
  • fix.diff β€” adds if (esize <= 0 || n <= 0) return (NULL); at fwdma_malloc_multiseg + defense-in-depth check in fwdev_allocbuf.
  • build.log / run.log / env.txt β€” captured outputs.
VERDICT.md verdict full narrative: mechanism + impact ceiling + fix
↓ download raw

VERDICT β€” DF-1116

Verdict: REPRODUCED (primitive) / NOT REACHABLE on guest (HW-gated)

The cited bug is real and confirmed by source trace + userspace arithmetic reproduction. It is a latent kernel-panic primitive: reachable only when a FireWire controller is attached, which this audit guest does not have.

Mechanism (confirmed path:line)

  1. FW_SSTBUF ioctl (sys/bus/firewire/fwdev.c:504-506): user-supplied ibufreq is bcopy'd into d->bufreq with no validation of psize/nchunk/npacket. A local user in group operator (device perms 0660 root:operator per fwdev.c:173-175) can set psize = 0.
  2. FW_STSTREAM / FW_SRSTREAM (fwdev.c:447 / :475) β†’ fwdev_allocbuf(fc, ir/it, &d->bufreq.tx/rx).
  3. fwdev_allocbuf (fwdev.c:107): b->psize = roundup2(b->psize, 4) β€” identity for 0. Passes b->psize as esize to fwdma_malloc_multiseg (fwdev.c:108-109).
  4. fwdma_malloc_multiseg (fwdma.c:159): ssize = rounddown(PAGE_SIZE, esize) β†’ (4096 / 0) * 0 β†’ #DE trap.
  5. Trap β†’ panic at sys/platform/pc64/x86_64/trap.c:1101.

No priv_check on the ioctl path; only file-permission gating.

Reproduction (userspace harness)

The harness reproduces the arithmetic primitive in userspace. volatile defeats constant-folding (div-by-zero is UB; gcc would fold it at -O2 without this). On x86, integer div-by-zero raises SIGFPE (the user-mode analogue of kernel #DE):

SIGFPE: integer divide by zero -- primitive reproduced.
In kernel context this is a #DE trap -> panic (trap.c:1101).

Impact ceiling

  • Per-call: deterministic kernel panic (DoS). Single-threaded, 100% reproducible. No memory-corruption primitive β€” the trap fires before any write completes.
  • Privilege boundary: operator group β†’ kernel panic. Not unprivβ†’root; not even unprivβ†’DoS unless the user is in operator or the device is world-writable.
  • Realistic: requires a FireWire controller attached. Modern systems rarely have FW; legacy/scientific imaging setups do.

Fix

fix.diff adds two guards: 1. fwdma_malloc_multiseg (fwdma.c): if (esize <= 0 || n <= 0) return (NULL); at the top β€” rejects the degenerate request before the divide. 2. fwdev_allocbuf (fwdev.c): if (b->psize == 0 || b->nchunk == 0 || b->npacket == 0) return (EINVAL); β€” defense-in-depth at the caller.

Validated: firewire.ko builds with rc=0 after applying the fix.

Fix validation

  • Patch applies cleanly: Hunk #1 succeeded at 150 (fwdma.c) + Hunk #1 succeeded at 105 (fwdev.c).
  • make in sys/bus/firewire/ β†’ firewire.ko linked, RC=0.
  • Cannot boot-test the fix (no FireWire HW on guest); fix_status: not_testable per the HW-gated allowance. The fix compiles and is a trivial guard.

PoC changes

  • harness.c written from scratch (no prior PoC). Reproduces the arithmetic primitive; volatile prevents the compiler from eliding the div-by-zero.

Fix verification

not_testable

compile validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. fwdma_malloc_multiseg rounddown(PAGE_SIZE,0) -> #DE. No FireWire HW. Module compiles.