DF-3932 / verdict.json
{ "finding_id": "DF-3932", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix 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.", "fix_evidence": "findings/poc/DF-3932/fix.diff + VERDICT.md", "status": "inconclusive", "reproduced": 0, "impact": "panic", "confidence": "certain", "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_loads 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.", "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": "panic.txt + run.log (guest panic trace through _kmalloc); build.log (compilers own -Walloc-size-larger-than at the exact size); kmallocsink_replica.c; fix.diff", "kernel_refs": [ "sys/dev/raid/mly/mly.c:2889", "sys/dev/raid/mly/mly.c:2890", "sys/dev/raid/mly/mly.c:2891", "sys/dev/raid/mly/mlyio.h:42", "sys/dev/raid/mly/mly.c:1850", "sys/kern/kern_slaballoc.c:942", "sys/kern/kern_slaballoc.c:1724", "sys/platform/pc64/x86_64/busdma_machdep.c:767" ], "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.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 420, "guest_dirty": 0, "notes": "Guest was panicked by the replica and reset to with-src afterwards (final guest_dirty=0). fix not validated on a mly kernel build (hardware-gated trigger); the replica demonstrates the size never reaching kmalloc removes the panic.", "recommended_fix": "Reject DataTransferLength == INT_MIN and cap |DataTransferLength| at MAXBSIZE (the buffer DMA tag maxsize).", "build_cmd": "cd /root/kmod/ksink && make", "run_cmd": "kldload /root/kmod/ksink/kmallocsink.ko; sysctl kern.kmallocsink=1", "code_hash": "fc50eca3140f6841a490a6c8c83fb450665adde6d1cf9e47ef32a4a8b77c93ad" } |