{
  "finding_id": "DF-2865",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Reproduced on the 6-cpu stock INVARIANTS guest with a KLD harness that gives getpbuf_kva()/relpbuf() a per-subsystem reservation counter of cap 1 hammered by one kernel thread per CPU: the counter was observed NEGATIVE (vm.pbres_min=-1, vm.pbres_neg=2 in 10 s, console 'PBUFRES: counter NEGATIVE (-1)') and TWO pbufs were held simultaneously against the cap-1 reservation (vm.pbres_maxinflight=2), proving the <=0 gate at vm_pager.c:463 is not atomic with the decrement at :489-490 (per-bucket spinlocks give no cross-CPU exclusion for the counter). This is the allocator-side atomicity hole; it is distinct from known DF-0953 (release-side wake thresholds in relpbuf), though both manifest as unprivileged I/O latency (vfs_cluster.c mnt_pbuf_count / v_pbuf_count and swap_pager.c nsw_rcount are the real-world counters). fix.diff (atomic_cmpset_int reservation loop in all five allocators, buffer put back on lost race) validated on an in-guest rebuilt kernel #1: same harness shows neg=0, min=0, maxinflight=1 and clean unload.",
  "exploit_chain": "unpriv user: (1) generate concurrent clustered file I/O (reads/writes on any filesystem, vfs_cluster.c:924-927/1525-927 trypbuf_kva/getpbuf_kva against mnt_pbuf_count or v_pbuf_count) and/or force swap-in under memory pressure (swap_pager.c:1447 getpbuf_kva(&nsw_rcount)); (2) when such a counter sits near 0 under load, CPUs racing through the unlocked gate over-decrement it: the subsystem transiently exceeds its pbuf cap (over-consumption of the shared kva/raw pools, squeezing unrelated subsystems) and the counter dips negative, so getpbuf sleepers block on *pfreecnt<=0 while pbufs are actually free, extending stalls already made likely by DF-0953's wake thresholds; (3) self-correcting per release, so ceiling is local I/O-latency DoS, not memory corruption.",
  "evidence": [
    "run.2.log: 'vm.pbres_neg: 2 / vm.pbres_min: -1 / vm.pbres_maxinflight: 2' + console 'PBUFRES: counter NEGATIVE (-1), cap=1, inflight=1'",
    "run.log: independent stock boot, same numbers; also shows 5 threads asleep on the reservation gate while vm.pbuf_kva_count=512 (pool 100% idle) - the known DF-0953 stall semantics this finding compounds",
    "run.patched.log (kernel #1 with fix.diff): 'vm.pbres_neg: 0 / vm.pbres_min: 0 / vm.pbres_maxinflight: 1', clean rescue+unload",
    "build.log / fix_build.log: module build and full nativekernel build logs"
  ],
  "kernel_refs": [
    "sys/vm/vm_pager.c:417",
    "sys/vm/vm_pager.c:418",
    "sys/vm/vm_pager.c:444",
    "sys/vm/vm_pager.c:445",
    "sys/vm/vm_pager.c:463",
    "sys/vm/vm_pager.c:489",
    "sys/vm/vm_pager.c:490",
    "sys/vm/vm_pager.c:512",
    "sys/vm/vm_pager.c:538",
    "sys/vm/vm_pager.c:539",
    "sys/vm/vm_pager.c:570",
    "sys/vm/vm_pager.c:578",
    "sys/vm/vm_pager.c:602",
    "sys/vm/vm_pager.c:610",
    "sys/vm/swap_pager.c:1447",
    "sys/kern/vfs_cluster.c:924",
    "sys/kern/vfs_cluster.c:1525"
  ],
  "poc_changes": "PoC authored from scratch (no seed). Iterations: (1) volatile-qualified counters tripped -Werror=cast-qual in the module build -> plain int + atomics, matching kernel style; (2) first stock run wedged kldunload because cap=1 also exercises the known DF-953 wake-threshold stall (all sleepers gated, pbuf pool idle) -> added vm.pbres_bump rescue sysctl (pbuf_adjcount-style add+wakeup) for clean teardown; harness semantics unchanged.",
  "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": 75,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh   (KMOD=pbufres built in-guest against /usr/src/sys)",
  "run_cmd": "sh run.sh     (kldload pbufres.ko; 10 s spin; sysctl vm.pbres_neg/min/maxinflight; sysctl vm.pbres_bump=100; kldunload pbufres)",
  "code_hash": "782a53b8abea0ab38509f1d54651f52d5a16badc44da66bb117042319dc268b1",
  "notes": "Both baseline boots (run.log, run.2.log) show identical stats; fix validation combined with the DF-2866 pagertab hunk in one nativekernel build (fix_build.log). Guest reset with-src afterwards, stock kernel #0 back, guest up. mnt_pbuf_count is additionally u_int type-punned through the int* API (sys/sys/mount.h:221) - benign on LE, noted for the record. vnode_pbuf_freecnt (vnode_pager.c:91) is written but never read anywhere - dead.",
  "recommended_fix": "Reserve the pfreecnt slot atomically at the point of dequeue: cmpxchg loop re-validating c>0 before c-1 (atomic_cmpset_int), put the buffer back on the free list and re-enter the gate when the reservation is lost; applied to getpbuf/getpbuf_kva/getpbuf_mem/trypbuf/trypbuf_kva - see fix.diff.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep  2 14:41:38 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Applied fix.diff to /usr/src in-guest (patch -p1, 6/6 hunks incl. the DF-2866 hunk), make -j6 nativekernel KERNCONF=X86_64_GENERIC + installkernel, rebooted into kernel #1. Exact same harness: baseline neg=2/min=-1/maxinflight=2; patched neg=0/min=0/maxinflight=1 with clean rescue/unload and normal ssh/dmesg operation. Reservation invariant holds; no regression seen.",
  "fix_evidence": [
    "findings/poc/DF-2865/run.patched.log: 'vm.pbres_neg: 0 / vm.pbres_min: 0 / vm.pbres_maxinflight: 1' + 'PBUFRES: done. neg=0 min=0 maxinflight=1'",
    "findings/poc/DF-2865/fix_build.log: full nativekernel build log (BUILD-OK)",
    "findings/poc/DF-2865/fix.diff: git-apply-able (verified --check against pristine sys/)"
  ]
}
