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)
PoC verification
Evidence pack
findings/poc/DF-1116 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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β addsif (esize <= 0 || n <= 0) return (NULL);atfwdma_malloc_multiseg+ defense-in-depth check infwdev_allocbuf.build.log/run.log/env.txtβ captured outputs.
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)
FW_SSTBUFioctl (sys/bus/firewire/fwdev.c:504-506): user-suppliedibufreqisbcopy'd intod->bufreqwith no validation ofpsize/nchunk/npacket. A local user in groupoperator(device perms0660 root:operatorperfwdev.c:173-175) can setpsize = 0.FW_STSTREAM/FW_SRSTREAM(fwdev.c:447/:475) βfwdev_allocbuf(fc, ir/it, &d->bufreq.tx/rx).fwdev_allocbuf(fwdev.c:107):b->psize = roundup2(b->psize, 4)β identity for 0. Passesb->psizeasesizetofwdma_malloc_multiseg(fwdev.c:108-109).fwdma_malloc_multiseg(fwdma.c:159):ssize = rounddown(PAGE_SIZE, esize)β(4096 / 0) * 0β #DE trap.- 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:
operatorgroup β kernel panic. Not unprivβroot; not even unprivβDoS unless the user is inoperatoror 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). makeinsys/bus/firewire/βfirewire.kolinked,RC=0.- Cannot boot-test the fix (no FireWire HW on guest);
fix_status: not_testableper the HW-gated allowance. The fix compiles and is a trivial guard.
PoC changes
harness.cwritten from scratch (no prior PoC). Reproduces the arithmetic primitive;volatileprevents the compiler from eliding the div-by-zero.
Fix verification
not_testablecompile 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.
No comments yet.