⬢ DragonFlyBSD Kernel Audit
DF-4926 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4926",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED twice (identical crash site) and FIX VALIDATED dynamically. nvmm_dragonfly.c tears down the hypervisor BEFORE killing the device: nvmm_detach() checks nmachines unsynchronized (:369) and runs nvmm_fini() (:372) \u2014 lockuninit of all 128x128 machine/vcpu locks, impl->fini(), nvmm_impl=NULL \u2014 while /dev/nvmm is still dispatching ioctls; destroy_dev() only runs afterwards (:404). An unprivileged group-nvmm user storming NVMM_IOC_CAPABILITY (~1.1M/s) + machine create/destroy cycles (~20k/s) against a root kldunload panicked the stock guest twice at nvmm_ioctl+0x444: movq 0x48(%rax),%rax (rax = nvmm_impl = NULL). With fix.diff (open-count gate + destroy_dev-first ordering), the identical race produced 40/40 EBUSY, zero panics over 36M ioctls / 684k machine cycles, and a clean final unload.",
  "exploit_chain": "",
  "evidence": "panic.txt (two baseline serial-console crash excerpts, both Stopped at nvmm_ioctl+0x444: movq 0x48(%rax),%rax, guest down); run.log (racer calibration: 1.1M cap ioctls/s, 20k machine cycles/s as uid 1002); race_driver_fixed.log + run_fixed_racer.log + fix_run.log (patched module: 40/40 EBUSY, no panic, clean final unload); fix_build.log (patched module build)",
  "kernel_refs": [
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:325",
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:366",
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:369",
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:372",
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:399",
    "sys/dev/virtual/nvmm/nvmm_dragonfly.c:404",
    "sys/dev/virtual/nvmm/nvmm.c:994",
    "sys/dev/virtual/nvmm/nvmm.c:1005",
    "sys/dev/virtual/nvmm/nvmm.c:245",
    "sys/kern/kern_conf.c:346"
  ],
  "poc_changes": "No executable seed (fresh racer + driver authored). Fixes made while bringing it up: (1) su -m executes -c via roots csh -> Ambiguous output redirect; replaced with a #!/bin/sh wrapper script; (2) racer opens /dev/nvmm O_RDONLY \u2014 0640 gives group read-only and DFly ioctls need no FWRITE (O_RDWR got EACCES); (3) driver tolerant of an already-loaded module.",
  "attempts": 3,
  "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": 1800,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -pthread -o nvmm_race nvmm_race.c   (guest, as root; full setup: sh run.sh)",
  "run_cmd": "sh run.sh   (creates testnv, builds racer, kldload nvmm, races 40 unload/reload cycles + final unload; baseline panics, patched returns EBUSY/clean-unload)",
  "code_hash": "f3a5459309fb97ed183e32d7ea488f64e44d6d7d572eddbdf662e6221c6509f8",
  "notes": "Requires the guest to see VMX/SVM (QEMU_EXTRA=-cpu host); backend x86-vmx attached. Unprivileged side needs only group nvmm (device 0640 root:nvmm, O_RDONLY suffices for all ioctls). UAF paths (2)/(3) \u2014 machine created between the :369 check and nvmm_fini(), and dtor/kill_machines through unmapped text after a successful unload \u2014 are code-proven but were not needed for the reproduction (path 1 panicked deterministically). Guest reset (with-src) performed after the run.",
  "recommended_fix": "Count open descriptors, refuse MOD_UNLOAD while any are open (or machines exist), and destroy_dev() BEFORE nvmm_fini() \u2014 see fix.diff.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 1,
  "fix_verdict": "FIXED dynamically: baseline (stock nvmm.ko) panicked the guest twice at nvmm_ioctl+0x444 under the race; with fix.diff applied to guest /usr/src (module rebuilt, fix_build.log) the identical workload produced 40/40 kldunload EBUSY (open-count gate), zero panics across 36M capability ioctls and 684k machine create/destroy cycles, and a clean final unload after the racers fd closed (race_driver_fixed.log, run_fixed_racer.log, fix_run.log).",
  "fix_evidence": "fix.diff (git-apply clean); fix_build.log (BUILD_RC=0); fix_run.log (40x EBUSY vs baseline panic, clean final unload, guest up); panic.txt (baseline crashes)"
}