{
  "finding_id": "DF-2680",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Use-after-free of devsoftc.async_proc reproduced on the stock kernel with a visible, benign manifestation: a root process opened /dev/devctl, issued FIOASYNC=1 (devioctl stores raw curproc, subr_bus.c:412-417, no reference) and exited; devclose (subr_bus.c:352-361) left async_proc dangling after the struct proc was reaped and kfree'd (kern_exit.c:1336). The next fork recycled the freed chunk, and every subsequent devctl event (device attach/detach -> devctl_queue_data, subr_bus.c:521-523) executed ksignal() on the recycled memory: lwpsignal() ran PHOLD(p) and lwkt_gettoken(&p->p_token) (kernel writes through the dangling pointer) and delivered SIGIO to a process that never opened /dev/devctl. 5/5 rounds reproduced (catcher pid = victim pid + 1 each time); a live-path control (victim stays alive, handler installed) confirmed the FIOASYNC->ksignal delivery path first. Trigger requires root to open the 0600 RESTRICTEDROOT-gated device, hence Low severity despite the kernel-memory-write primitive.",
  "exploit_chain": "root opens /dev/devctl + FIOASYNC -> exits without clearing -> async_proc dangles -> any later devctl event ksignal()s freed memory -> PHOLD/token writes into whatever recycled the chunk (cross-process SIGIO demonstrated; foreign-object corruption if recycled by non-proc data). No uid0 chain (root-gated setup).",
  "evidence": "run.log (control GOT_SIGIO pid=1961 live path; stale rounds GOT_SIGIO pid=1994/2032/2048/2075/2091 'never opened devctl'); victim.c/victim2.c/catcher.c; VERDICT.md; fix.diff",
  "kernel_refs": [
    "sys/kern/subr_bus.c:405",
    "sys/kern/subr_bus.c:412",
    "sys/kern/subr_bus.c:352",
    "sys/kern/subr_bus.c:521",
    "sys/kern/subr_bus.c:336",
    "sys/kern/kern_exit.c:1336"
  ],
  "poc_changes": "PoC authored fresh. Iterations: initial runs showed no SIGIO because (a) SIGIO's default disposition is ignore, so both the control and the catcher need handlers installed, and (b) the DF-2679 churn module's children never attached, so no devadded/devremoved devctl events fired at all - fixed by adding a real driver to the churn module; devd must be stopped because /dev/devctl allows a single reader.",
  "attempts": 4,
  "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": 240,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/df2680 && cc -O2 -o victim victim.c && cc -O2 -o victim2 victim2.c && cc -O2 -o catcher catcher.c",
  "run_cmd": "sh run_seq.sh   (root; needs dfrace.ko from DF-2679 as devctl event source)",
  "code_hash": "c03a16480fd02c67be4f0c192e98fe92fd1118777da16f24d6c5128b13a06377 (victim.c)",
  "notes": "Not fix-validated by rebuild (Low severity): fix is the two-line close-path clear in fix.diff plus optional PHOLD/PRELE reference handling; reasoned correctness covered in VERDICT.md. fix_status=inconclusive for the record.",
  "recommended_fix": "devclose(): devsoftc.async_proc = NULL under devsoftc.lock (and hold a PHOLD/PRELE reference on the stored proc in devioctl if SIGIO-after-exit is ever desired)."
}
