⬢ DragonFlyBSD Kernel Audit
DF-3932 / verdict.json
← back to finding ↓ download raw
{
  "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"
}