MLYIO_COMMAND size handling: DataTransferLength==INT_MIN negates/wraps into kmalloc(0xFFFFFFFF80000000, M_INTWAIT) β kernel panic (sink PROVEN on guest); plus unbounded wired allocation and ignored dmamap_load failure
Summary
Signed int DataTransferLength negated wraps for INT_MIN, sign-extends to size_t 0xFFFFFFFF80000000, passes the >0 guard and reaches kmalloc(M_INTWAIT) which panics in kmem_slab_alloc (M_INTWAIT lacks M_NULLOK). SINK PROVEN on the guest via a replica kld: panic 'kmem_slab_alloc(): kernel_map ran out of space!' captured in the serial log. Also no upper bound (2GB-1 wired per ioctl) and an ignored bus_dmamap_load error. Root-only control device; robustness DoS. Reject-and-cap fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3932 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.3 KB | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| kmallocsink_replica.c | β | 2.2 KB | view raw | |
| build.log | β | 847 B | view raw | |
| run.log | β | 1.9 KB | view raw | |
| panic.txt | β | 599 B | view raw | |
| fix.diff | β | 900 B | view raw | |
| env.txt | β | 543 B | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-3932 β mly: MLYIO_COMMAND size handling β INT_MIN negation panics kmalloc; unbounded sizes; ignored dmamap_load failure
Build / Run
Sink proof (done, on the audit guest)
The exact kmalloc the ioctl path performs was executed via a kernel-module replica (kmallocsink_replica.c, in this pack):
build: see build.log (guest cc 8.3, -Werror; note the compiler itself flagged the size 18446744071562067968 as exceeding max object size β same value mly.c:2889 computes) run: kldload kmallocsink.ko; sysctl kern.kmallocsink=1 expected + observed: panic "kmem_slab_alloc(): kernel_map ran out of space!" (panic.txt, run.log) β guest died, reset afterwards.
Full trigger (hardware-gated, on a machine with /dev/mly0)
#include
Expected result
Kernel panic (proven at the sink level on this guest; the ioctl trigger itself needs the mly device node that only exists with the hardware).
See VERDICT.md.
DF-3932 verdict narrative
Reproduced?
The sink is reproduced on the audit guest kernel: executing the exact
kmalloc(0xffffffff80000000, M_DEVBUF, M_INTWAIT) that mly_user_command
performs for DataTransferLength == INT_MIN panics the kernel with
"kmem_slab_alloc(): kernel_map ran out of space!" (panic.txt; trace
fire_sysctl β _kmalloc β kmem_slab_alloc). The trigger (MLYIO_COMMAND
ioctl on /dev/mly0) is hardware-gated β no mly controller in the guest β so
the finding as a whole is filed inconclusive (mechanism proven, instance
hardware-gated), following the DF-3715 family convention.
Root cause chain (path:line)
- mlyio.h:42 β
DataTransferLengthis a plain signedint, fully user-controlled through the 0600 root:operator control device. - mly.c:2889
c mc->mc_length = (uc->DataTransferLength >= 0) ? uc->DataTransferLength : -uc->DataTransferLength;For INT_MIN the negation wraps (int overflow) and the sign-extension intosize_t mc_lengthyields 0xFFFFFFFF80000000 β which satisfiesmc->mc_length > 0at mly.c:2890. - mly.c:2891
mc->mc_data = kmalloc(mc->mc_length, M_DEVBUF, M_INTWAIT);β kern_slaballoc.c:942size >= ZoneLimitβ kmem_slab_alloc β vm_map_findspace fails β kern_slaballoc.c:1724 panic (M_INTWAIT has no M_NULLOK). Proven on-guest (panic.txt). - Secondary defects in the same size handling (source-certain, folded here):
- No upper bound at all: any root user can request up to 2GB-1 of WIRED
kernel memory per ioctl (M_INTWAIT β kmem_slab_alloc wires it),
repeatable β trivial kernel-memory exhaustion by the management daemon.
- mly.c:1850 ignores bus_dmamap_load()'s error; on this DFly bus_dma the
callback is always invoked (busdma_machdep.c:767-805) with the error
passed in, and mly_map_command_sg (mly.c:1781) ignores its
errorargument too β a failed/partial load (EFBIG for >MAXBSIZE buffers, which the missing cap makes reachable) still dispatches the command with whatever stale s/g entries the slot's table holds.
Threat model
Root-only device node β the panic/DoS primitive is already-privileged (severity Low accordingly); the defect matters for robustness of the management stack (linux-mly-compatible tools pass user lengths straight through) and because the panic is reachable from a single ioctl with no hardware preconditions beyond the card being present.
Impact
Kernel panic (DoS), proven at the sink on this guest kernel; wired-memory exhaustion variant; stale-s/g dispatch variant.
Fix
Reject INT_MIN and cap |DataTransferLength| at MAXBSIZE (the buffer DMA tag's maxsize) β fix.diff.
Fix validation
Not run (fix touches mly path; hardware-gated). The kmallocsink replica already demonstrates that the size never reaching kmalloc removes the panic; the cap also makes bus_dmamap_load's EFBIG path unreachable.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableFix not validated on the guest: the trigger path requires mly/age hardware absent from the audit guest. fix.diff is mechanical (bounds/termination-only) and authored post-verification against the cited lines.
findings/poc/DF-3932/fix.diff + VERDICT.md
Confirmed kernel references
Detail
Exploit chain
root opens /dev/mly0, issues MLYIO_COMMAND with DataTransferLength=INT_MIN -> kmalloc(0xFFFFFFFF80000000, M_INTWAIT) -> kmem_slab_alloc panic (sink proven on guest via replica)
Evidence (decisive lines)
panic.txt + run.log (guest panic trace through _kmalloc); build.log (compiler's own -Walloc-size-larger-than at the exact size); kmallocsink_replica.c; fix.diff
PoC changes
Authored kmallocsink kernel-module replica (guest has no mly hardware); the /dev/mly0 trigger program is documented in README.md. Replica v1 was rejected by -Werror (the compiler flagging the overflowing negation is itself evidence); v2 uses a volatile to make the size opaque - behavior identical.
Verified recommended fix
Reject DataTransferLength == INT_MIN and cap |DataTransferLength| at MAXBSIZE (the buffer DMA tag maxsize).
Verdict
Mechanism PROVEN on the guest kernel at the sink level; the ioctl trigger itself is hardware-gated (no /dev/mly0 without the card) -> inconclusive per family convention, with the panic observed. mly_user_command (mly.c:2889) computes mc_length = -DataTransferLength for negative int lengths; for DataTransferLength == INT_MIN (mlyio.h:42: signed int) the negation wraps and sign-extends to size_t 0xFFFFFFFF80000000, which passes the mc_length > 0 guard (mly.c:2890) and reaches kmalloc(M_INTWAIT) (mly.c:2891). Guest replica executing that exact call (kmallocsink_replica.c): panic 'kmem_slab_alloc(): kernel_map ran out of space!' with trace fire_sysctl -> _kmalloc -> kmem_slab_alloc (panic.txt, run.log; kern_slaballoc.c:1724 - M_INTWAIT carries no M_NULLOK). The guest compiler independently flagged the size as exceeding maximum object size during build. Secondary, same locus (folded): no upper bound on DataTransferLength (up to 2GB-1 WIRED kernel memory per ioctl, root-gated), and bus_dmamap_load's failure is ignored at mly.c:1850 (on DFly the callback always runs with the error - busdma_machdep.c:767-805 - and mly_map_command_sg ignores it too), so oversized/unloadable buffers still dispatch with stale s/g entries. Root-only device (0600) -> Low severity.
No comments yet.