{
  "finding_id": "DF-2879",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "swaponvp() rounds each swap device's page count UP to a SWB_DMMAX (64-page) stripe multiple and frees the entire last stripe into the global blist, registering up to 63 pages per device that do not exist on the device. Any pageout the pager directs at a phantom block is rejected by the disk layer (EINVAL) and the pageout daemon wedges in an unbounded error-retry livelock. Reproduced twice from fresh resets on the stock kernel: a 65-page vn device registered as 128 pages; under memory pressure the serial console accumulated 25,712 and 18,215 'swap_pager: I/O error - pageout failed; offset 1368064..., error 22' lines (offset = device page 78 of a 65-page device) and the guest went down both times. swapon requires SYSCAP_RESTRICTEDROOT, so this is a root-misconfiguration-triggered full-system availability loss, not a privilege-boundary crossing; no memory corruption or disclosure (read errors set m->valid=0 -> SIGBUS; write errors re-dirty in place).",
  "exploit_chain": "",
  "evidence": [
    "trigger.sh + run.log: real device 0x41000 bytes (520 512-blocks) registered as 1024 512-blocks; vm.swap_size gains 64 pages of which 1 is real",
    "wedgelog.txt: 25,712 / 18,215 unbounded 'swap_pager: I/O error - pageout failed; offset 1368064,error 22' iterations; vm.sh status -> down after both runs",
    "fix_run.log: patched kernel registers the real 520 blocks, gains exactly 1 page, and under the identical pressure scenario produces ZERO I/O errors while staying responsive",
    "fix.diff: replace the round-up with the real page-truncated count so the pre-existing min() clips in both the swapon free-loop (vm_swap.c:378) and swapoff fill-loop (vm_swap.c:540) take effect"
  ],
  "kernel_refs": [
    "sys/vm/vm_swap.c:365",
    "sys/vm/vm_swap.c:367",
    "sys/vm/vm_swap.c:377",
    "sys/vm/vm_swap.c:128",
    "sys/vm/vm_swap.c:539",
    "sys/vm/swap_pager.c:537",
    "sys/vm/swap_pager.c:1855",
    "sys/vm/swap_pager.c:1947",
    "sys/vm/swap_pager.c:1965",
    "sys/kern/subr_disk.c:1246"
  ],
  "poc_changes": "trigger/run scripts assembled from the live session (vn 266240-byte file -> vnconfig -> swapctl -a; hog recompiled per reset because /tmp is wiped on boot)",
  "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": 2100,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -o /tmp/hog hog.c",
  "run_cmd": "./trigger.sh && ./run.sh",
  "code_hash": "1be25ac756cdea8e03434d6784e27f45a189f56ec8f94c4c585524622a22befa",
  "notes": "Baseline runs: stock kernel wedged twice (guest down both times, recovered with vm.sh reset with-src). Fixed kernel: make -j6 nativekernel rc=0, 0 errors; identical PoC -> zero errors, graceful 'swap full' degradation, clean swapoff of the unaligned device afterwards. sw_nblks int32 truncation for >=2^31-page devices tracked separately (DF-2880).",
  "recommended_fix": "In swaponvp() use the real page-truncated block count (aligned_nblks = nblks) instead of rounding up to SWB_DMMAX, letting the existing min() clip the final partial stripe; see fix.diff",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Wed Sep  2 20:43:17 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel built in-guest (rc=0, 0 errors) and booted. Registration phantom gone (vn1 = real 520 blocks, vm.swap_size +1 page). Identical pressure scenario (primary swap removed, 5.2 GB hog): zero swap_pager I/O errors, guest responsive, clean recovery without reset; unaligned device also swaps off cleanly. Baseline stock kernel: same scenario produced 25k+/18k+ error iterations and a wedged, unresponsive guest twice.",
  "fix_evidence": [
    "findings/poc/DF-2879/fix.diff",
    "findings/poc/DF-2879/fix_build.log",
    "findings/poc/DF-2879/fix_run.log"
  ]
}
