Unclamped si_iosize_max on syspd allows oversized bio to permanently stall controller
Summary
mfi_syspd_attach (mfi_syspd.c:135-136): si_iosize_max=mfi_max_io*secsize with NO upper bound. Sibling mfi_disk.c:150-152 clamps to min(mfi_max_io*secsize,(mfi_max_sge-1)*PAGE_SIZE). mfi_max_io up to ~1000+ sectors ~512KB; (mfi_max_sge-1)*PAGE_SIZE typically ~128KB (mfi_max_sge=min(fw,33) mfi.c:453). DMA tag nsegments=mfi_max_sge (mfi.c:552). Bio needing >32 segments -> bus_dmamap_load EFBIG in mfi_mapcmd (mfi.c:2161) -> mfi_startio requeues cm forever (mfi.c:2145-2148 break loop) -> permanent controller-wide I/O stall. Raw /dev physio bounded MAXPHYS=128KB usually does NOT trigger; realistic trigger: GEOM consumer (vinum/gmirror/swap) or fs issuing >MAXPHYS bios. AV:L/PR:L/AC:L, A:H. Fix: clamp si_iosize_max=min(mfi_max_io*secsize,(mfi_max_sge-1)*PAGE_SIZE) matching mfi_disk.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2067 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full narrative, mechanism, citations, fix-build result | 3.7 KB | β raw |
| README.md | readme | evidence-pack overview | 1.6 KB | β raw |
| fix.diff | suggested-fix | standalone git-apply-able fix (authored post-verification) | 563 B | view raw |
| build.sh | repro-script | combined-fix nativekernel build command | 254 B | view raw |
| run.sh | repro-script | runtime trigger (HW/module-gated) | 415 B | view raw |
| fix_build.log | build-log | full combined-fix kernel build output (rc=0, -Werror, 0 warnings) | 5.6 MB | β download |
| env.txt | environment | guest uname / kern.version / cc version | 289 B | view raw |
DF-2067 β PoC evidence pack
Finding: Unclamped si_iosize_max on syspd allows oversized bio to stall controller Class: Uncontrolled resource consumption / DoS (CWE-770) | Impact ceiling: dos | Confidence: speculative
Status
SOURCE-CONFIRMED. The defect is verified real by line-by-line tracing of the
audited sys/ tree, but it is HW/module-gated: the audit guest lacks the
required hardware/module (LSI MegaRAID (mfi) controller in syspd mode + a GEOM consumer (vinum/gmirror/swap) or fs issuing >MAXPHYS bios), so it cannot be triggered at runtime
here. See VERDICT.md for the full mechanism and citations.
How to reproduce (on a guest that HAS the hardware)
- Ensure the gating precondition is met: LSI MegaRAID (mfi) controller in syspd mode + a GEOM consumer (vinum/gmirror/swap) or fs issuing >MAXPHYS bios.
- Apply
fix.diffto confirm the cited path changes; before the fix the cited code path exhibits the behaviour described inVERDICT.md. - The original finding markdown describes the runtime trigger.
Build validation (Phase 8)
fix.diff applies cleanly to pristine source. All nine findings' fixes were
built together; see VERDICT.md "Phase 8" and fix_build.log:
make -j6 nativekernel KERNCONF=X86_64_GENERIC # rc=0, -Werror, 0 warnings
Files
VERDICT.mdβ full narrative, mechanism, citations, fix-build resultfix.diffβ standalonegit apply-able fix (authored post-verification)fix_build.logβ full combined-fix kernel build output (rc=0, -Werror)env.txtβ guest environment for this verificationmanifest.jsonβ machine-readable catalog
DF-2067 β VERDICT
Verdict: REPRODUCED (source-confirmed; HW/module-gated β not runtime-triggerable on this guest) Class: Uncontrolled resource consumption / DoS (CWE-770) Impact ceiling: dos Confidence: speculative
Reproduction status
This finding is HW/module-gated and could not be triggered at runtime on the
audit guest. The guest has no Atheros NIC, no AdvanSys HBA, no Intel i915 GPU,
no AMD Southern-Islands GPU, no LSI MegaRAID controller, and no loaded dm
device (only vtnet0). The PoC therefore cannot reach the vulnerable path at
runtime here. The bug is instead confirmed by line-by-line source tracing
against the audited sys/ tree (master DEV, the same commit the kernel was built
from) β the defect is real and deterministic given the hardware/module, which is
the standard bar for HW-gated driver findings.
Mechanism (source-confirmed)
mfi_syspd_attach() sets sc->pd_dev_t->si_iosize_max = mfi_max_iosecsize with NO upper bound (mfi_syspd.c:135-136). The sibling LD path clamps to min(mfi_max_iosecsize, (mfi_max_sge-1)PAGE_SIZE) (mfi_disk.c:150-152). On the success path mfi_max_io comes from the firmware (ci->max_request_size) at mfi.c:964, NOT from the (mfi_max_sge-1)PAGE_SIZE derivation, so mfi_max_iosecsize can exceed the scatter-gather capacity. A bio larger than (mfi_max_sge-1)PAGE_SIZE then needs >mfi_max_sge segments -> bus_dmamap_load EFBIG in mfi_mapcmd -> mfi_startio requeues the command forever -> controller-wide I/O stall.
Gating precondition: LSI MegaRAID (mfi) controller in syspd mode + a GEOM consumer (vinum/gmirror/swap) or fs issuing >MAXPHYS bios
Exploit chain
Not applicable β this finding is HW/module-gated on the audit guest (no
triggering hardware/module present), so no userspace-driven escalation chain can
be exercised here. Per AGENT.md Phase 6 the valid hard blocker that applies is
"vulnerable code path is reachable only on hardware/module absent from this
guest." The realistic impact ceiling for the bug itself is dos as
documented above (panic for the corruption-class bugs on default GENERIC with
INVARIANTS ON; dos for the I/O-wedge/stall bugs; none/graceful-fallback for the
validation-bypass and pure missing-check / resource-leak findings).
Fix (authored, git-apply-able)
Clamp si_iosize_max in mfi_syspd_attach() to min(mfi_max_iosecsize, (mfi_max_sge-1)PAGE_SIZE), matching the sibling mfi_disk.c LD path.
The standalone diff is fix.diff in this directory. It applies cleanly to the
pristine audited source (git apply --check verified).
Phase 8 β combined fix-build validation
All nine findings' fixes were applied together to a single in-guest source tree
and built with make -j6 nativekernel KERNCONF=X86_64_GENERIC. The DragonFly
kernel compile invokes cc ... -Werror on every translation unit, so this
is a true -Werror build.
- Result:
=== NK_DONE rc=0 ===(full log:fix_build.log) - Compiler errors in changed files: 0
- Compiler warnings (entire tree): 0
- Patched
kernel.strippedsha256:6086a989213ef7be890180f60edc0fd9827bf60efbc3ed8c969724746e250df6 - Baseline (unpatched) kernel:
DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026
Because the PoC is HW/module-gated, the fix could not be runtime A/B tested on this guest (no way to trigger the bad behaviour on either the baseline or the patched kernel). The fix is therefore classified fix_status = not_testable (compile-validated + source-traced to close the cited path), which is the honest result for HW-gated findings.
Kernel references (verified during this trace)
Fix verification
not_testableCOMPILE-VALIDATED, runtime not_testable. fix.diff applies cleanly and built into the combined nativekernel build: === NK_DONE rc=0 ===, -Werror, 0 warnings in mfi_syspd.c. Source-traced closure: si_iosize_max is now clamped to the SG capacity, matching mfi_disk.c. Runtime A/B impossible (no mfi controller).
baseline mfi_syspd.c:135 si_iosize_max = mfi_max_io*secsize (unclamped); patched: min(mfi_max_io*secsize,(mfi_max_sge-1)*PAGE_SIZE). Combined build === NK_DONE rc=0 ===, 0 warnings. Gating: no mfi controller => runtime not_testable.
Confirmed kernel references
Detail
Exploit chain
none (DoS via oversized-bio permanent requeue; not memory corruption). No escalation chain; impact ceiling is a controller-wide I/O stall when a consumer issues a bio exceeding the SG capacity.
Evidence (decisive lines)
mfi_syspd.c:135-136 si_iosize_max = mfi_max_io*secsize (no clamp); mfi_disk.c:150-152 min(mfi_max_io*secsize,(mfi_max_sge-1)*PAGE_SIZE) (clamped); mfi.c:964 mfi_max_io = min(max_sectors_1,max_sectors_2) (firmware-derived). Combined-fix build: === NK_DONE rc=0 === (-Werror).
PoC changes
findings/poc/DF-2067/ populated: VERDICT.md, fix.diff (clamp si_iosize_max = min(mfi_max_iosecsize,(mfi_max_sge-1)PAGE_SIZE) matching mfi_disk.c), README.md, build.sh, run.sh, env.txt, fix_build.log, manifest.json.
Verified recommended fix
In mfi_syspd_attach() (mfi_syspd.c) clamp si_iosize_max = min(sc->pd_controller->mfi_max_iosecsize, (sc->pd_controller->mfi_max_sge-1)PAGE_SIZE), matching the sibling mfi_disk.c LD path. Standalone diff in findings/poc/DF-2067/fix.diff; matches finding proposal.
Verdict
SOURCE-CONFIRMED (HW-gated). mfi_syspd_attach() sets sc->pd_dev_t->si_iosize_max = mfi_max_iosecsize with NO upper bound (mfi_syspd.c:135-136). The sibling LD path clamps to min(mfi_max_iosecsize, (mfi_max_sge-1)PAGE_SIZE) (mfi_disk.c:150-152). On the success path mfi_max_io comes from the firmware (ci->max_request_size) at mfi.c:964, NOT from the (mfi_max_sge-1)PAGE_SIZE derivation, so mfi_max_iosecsize can exceed the scatter-gather capacity. A bio larger than (mfi_max_sge-1)PAGE_SIZE then needs >mfi_max_sge segments -> bus_dmamap_load EFBIG in mfi_mapcmd -> mfi_startio requeues the command forever -> controller-wide I/O stall. Realistic trigger is a GEOM consumer (vinum/gmirror/swap) or fs issuing >MAXPHYS bios; raw /dev physio is usually bounded by MAXPHYS=128KB and does not trigger.
No comments yet.