{
  "finding_id": "DF-5829",
  "status": "reproduced",
  "verdict": "vm_object_page_clean() gates its PG_CLEANCHK marking pass (pass1) on wholescan (range == entire object from page 0), while pass2 and collect_flush skip every page lacking PG_CLEANCHK; therefore every partial-range clean silently flushes nothing and returns success. msync(MS_SYNC) maps 1:1 onto this function with no fallback. Verified on the stock kernel: on hammer2 (whose vnode objects are sized beyond EOF by nvnode_pager_setsize, so wholescan is unsatisfiable by any in-file range) ALL msync ranges performed zero vnode pageout (v_vnodeout/v_vnodepgsout deltas 0 across 3 deterministic reps); on UFS, partial-range and whole-mapping-of-larger-file msyncs were no-ops while whole-file msync flushed. Arm F (msync of a 16-page range on a 1-page file, i.e. exactly the block-rounded object->size) flushed on the stock kernel - mechanistic proof of the gate. One-line fix validated by rebuilding the kernel on the guest: with pass1 run unconditionally over the requested range, the previously-dead arms all flush (A=1/2 B=1/6 C=1/4 D=1/1, 3 reps).",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "exploit_chain": "",
  "evidence": "run.h2.log (hammer2 stock: A-E=0/0, F=1/1, 3 reps), run.3-ufs.log (UFS stock: A=0/0 B=1/4-6 C=0/0 D=1/1), run.patched-h2.log (patched: A=1/2 B=1/6 C=1/4 D=1/1 E=1/1 F=1/1), fix.diff, fix_build.log, VERDICT.md",
  "kernel_refs": [
    "sys/vm/vm_object.c:981",
    "sys/vm/vm_object.c:991",
    "sys/vm/vm_object.c:1039",
    "sys/vm/vm_object.c:1074",
    "sys/vm/vm_object.c:1175",
    "sys/vm/vm_map.c:3148",
    "sys/kern/vfs_vm.c:465",
    "sys/vm/vnode_pager.c:764"
  ],
  "poc_changes": "Seed was a description only. Wrote msync_poc.c from scratch: arms A-F as unprivileged child processes (dirty one MAP_SHARED page, msync window, sleep), parent samples vm.stats.vm.v_vnodeout/v_vnodepgsout (incremented only in vnode_pager_putpages, sys/vm/vnode_pager.c:764) around the syscall and SIGKILLs the child before teardown so exit cannot flush. First run was invalid: /tmp on the guest is tmpfs (swap-backed objects -> page_clean early-outs); second run on hammer2 showed even the controls flat, which led to the object-size-beyond-EOF discovery (nvnode_pager_setsize) and the beyond-EOF arms E/F that turn the hammer2 anomaly into mechanistic proof. UFS arms run under /boot with a root-placed directory.",
  "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 (baseline); patched #1: Tue Oct 13 09:06:21 UTC 2026 for fix validation",
  "runtime_sec": 1500,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o msync_poc msync_poc.c",
  "run_cmd": "./msync_poc 3 [/home/maxx/df5829 | /boot/df5829]",
  "code_hash": "b8d9010de6badf7437bcd74a72af47c529dc331ef57b101be5b6d4fcf757e20d",
  "notes": "Not memory corruption; no privesc chain applies. Data-integrity impact: msync(MS_SYNC) silently performs no writeback - on the default hammer2 root filesystem for EVERY range, on UFS for any range not exactly [page0, EOF]. Data survives only via the ~30s syncer; crash window loses data apps believe durable. Also breaks MS_SYNC|MS_INVALIDATE and the NFS server partial clean (nfs_serv.c:3663). Negative control run.log on tmpfs documents the type!=OBJT_VNODE early-out. Extra observation from pass 3 analysis: vm_object_pmap_copy declared at vm_object.h:323 has no definition or callers anywhere in sys/ (dead declaration, cosmetic).",
  "recommended_fix": "In vm_object_page_clean(), always run the PG_CLEANCHK marking pass1 over the requested range; keep OBJ_WRITEABLE/OBJ_MIGHTBEDIRTY/vclrobjdirty clearing gated on wholescan && !error (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Applied fix.diff to the guests /usr/src copy (host sys/ untouched), built with make -j8 nativekernel KERNCONF=X86_64_GENERIC (fix_build.log), rebooted into the patched kernel (#1 Oct 13 09:06:21) and re-ran the identical rig: the previously-flat arms all flushed deterministically (A=1/2 B=1/6 C=1/4 D=1/1 E=1/1 F=1/1 over 3 reps, run.patched-h2.log). The vulnerable behavior is gone; whole-object and beyond-EOF controls still flush as before. Guest reset to the clean-source snapshot afterwards.",
  "fix_evidence": "run.patched-h2.log, fix_build.log, fix.diff"
}