{
  "finding_id": "DF-4751",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced 4/4 on the QEMU guest (2 baseline + 2 with patched modules): a root-driven create/remove churn on /dev/mapper/control racing `kldunload dm` reliably panics the kernel with Fatal trap 12 executing freed module text (signatures: dev_dioctl+0x1b read of freed ops; IP=0x0 call through zeroed ops; freed-text exec at 0xffffffff82601029; dmioctl+0x2bf inside the freed handler with the sync_devs()-augmented fix installed). Root cause is two-fold: (1) dm_modcmd(MOD_UNLOAD) reads dm_dev_counter unlocked (device-mapper.c:161, race window here) vs dm_dev_insert()s locked increment (dm_dev.c:193) \u2014 every crash log shows malloc_uninit: 1984 bytes of dm still allocated proving devices outlived the check; (2) framework-level: dev_dioctl dispatches through dev->si_ops with no ops/module reference (kern_device.c:258) and destroy_devs dead_dev_ops revector is asynchronous (devfs_core.c:759), so an ioctl already executing dmioctl survives into freed text. Trigger requires root (kldunload) on one side; ioctl side alone is operator-group reachable \u2014 hence Low severity, panic impact.",
  "exploit_chain": "No unprivileged chain: kldunload requires root. Ceiling is a code-execution-class UAF (execution continues in freed kld text) groomed by root against itself; treated as correctness + framework hardening (gap affects every loadable driver, incl. nmdm.ko).",
  "evidence": "panic.txt (all four Fatal trap 12 signatures + malloc_uninit lines); run.log (40 benign unloads then crash); fix_build.log (patched dm.ko build); VERDICT.md fix-validation section (v1 destroy_dev+re-arm and v2 +sync_devs both still panic)",
  "kernel_refs": [
    "sys/dev/disk/dm/device-mapper.c:154",
    "sys/dev/disk/dm/device-mapper.c:161",
    "sys/dev/disk/dm/device-mapper.c:163",
    "sys/dev/disk/dm/device-mapper.c:190",
    "sys/dev/disk/dm/device-mapper.c:241",
    "sys/dev/disk/dm/dm_dev.c:193",
    "sys/kern/kern_device.c:237",
    "sys/kern/kern_device.c:258",
    "sys/vfs/devfs/devfs_core.c:759"
  ],
  "poc_changes": "Harness adapted from the DF-4473 proplib client (create/remove only, no targets); header comment de-nested to compile; include order fixed (sys/types.h first).",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 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": 480,
  "guest_dirty": 1,
  "build_cmd": "cc -O -Wall -o /tmp/dm_unloadrace dm_unloadrace.c -lprop -lpthread",
  "run_cmd": "kldload dm; /tmp/dm_unloadrace 30",
  "code_hash": "61dc9d24e54ca4e979c860cfda7744e30e87a1a7d8d510dd2791e5cd3fcfc653",
  "notes": "40+ unloads typically succeed benignly before the interleaving hits; every crashing run also printed the counter-race corroboration (malloc_uninit dm bytes). Guest reset with-src performed after the runs.",
  "recommended_fix": "Framework: reference dev_ops (and its module) across every dev_d* dispatch or make the revector synchronous + kldunload respect per-open-cdev module refs; driver-side shrink: fix.diff (destroy+sync_devs+stable counter+re-arm) plus an in-flight-ioctl drain that MOD_UNLOAD waits on.",
  "fix_status": "fix_failed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 1,
  "fix_verdict": "Two in-driver fixes were built as dm.ko and A/B-tested with the identical PoC: v1 (destroy_dev before counter check + re-arm on counter>0) still panicked (run 3, freed-text exec); v2 (v1 + sync_devs() per the kern_conf.c unload contract) still panicked INSIDE dmioctl (run 4, dmioctl+0x2bf) because sync_devs only drains the devfs message queue, not calls that already loaded si_ops->d_ioctl. The window is a dev-dispatch framework gap; fix.diff (v2) is kept as a documented best-effort shrink, NOT a complete fix.",
  "fix_evidence": "panic.txt runs 3 and 4; fix_build.log; VERDICT.md Why the in-driver fixes failed"
}