{
  "finding_id": "DF-5815",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "Deterministically reproduced (2/2 runs). With root-set vm.max_launder=-32, vm_pageout_scan_inactive()s budget math (sys/vm/vm_pageout.c:828-836) yields a NEGATIVE per-queue max_launder (-32/64=0 -> floor 1 -> cap test 1 >= -2 always true -> -2+1 = -1), so vm_pageout_page()s laundering branch (:1171, *max_launderp > 0) is permanently unreachable: dirty pages are never paged out. An unprivileged 3.4GB anonymous hog (uid 1001) then exhausts free+cache; the pagedaemon spins forever (pagedaemon: WARNING! Continuous paging for 1 minutes every minute on the serial console, 5x in run #1 / 3x in run #2), all vm_wait() sleepers block, ssh dies with Connection timed out during banner exchange, and only a hard reset recovers (both runs ended in vm.sh reset). Identical hog with stock vm.max_launder=4076: 39047 swap pager pageouts, hog completes in 61s, ssh alive, zero warnings. Severity Low only because the enabling write requires root; after the misconfiguration any unprivileged user freezes the machine and remote administration is lost until power-cycle.",
  "exploit_chain": "root (or a management agent) writes sysctl vm.max_launder=-32 -> per-queue launder budget negative -> pagedaemon can never clean dirty pages -> unprivileged user runs ordinary memory hog -> free+cache exhausts -> all allocations block in vm_wait forever -> console floods Continuous paging once/min -> ssh/remote admin dead -> hard reset required. No privilege escalation path (governor state only).",
  "evidence": [
    "findings/poc/DF-5815/control_vs_attack.txt \u2014 side-by-side: stock=39047 swap pageouts/hog completes; -32=frozen laundering + wedge",
    "findings/poc/DF-5815/panic_wedge_console.txt \u2014 run #1 serial console: 5x 'pagedaemon: WARNING! Continuous paging for 1 minutes'",
    "findings/poc/DF-5815/attack2_console.txt \u2014 run #2 (deliberate): 3x warning, ssh dead by t+170s",
    "findings/poc/DF-5815/observe_attack.log \u2014 swap pageouts frozen at 205 with -32 while fre sits at 80MB",
    "sys/vm/vm_pageout.c:828-836 \u2014 the unvalidated-division + cap inversion",
    "sys/vm/vm_pageout.c:1171 \u2014 '*max_launderp > 0' branch never taken"
  ],
  "kernel_refs": [
    "sys/vm/vm_pageout.c:828",
    "sys/vm/vm_pageout.c:836",
    "sys/vm/vm_pageout.c:1171",
    "sys/vm/vm_pageout.c:2707"
  ],
  "poc_changes": "none \u2014 the hog is a plain anonymous-memory touch loop; the decisive parameter (3400MB on 3.4GB usermem) was found by escalation from 2900MB (which left enough free+cache headroom for the daemon to idle at target).",
  "attempts": 2,
  "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": 900,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o df5815_hog df5815_hog.c",
  "run_cmd": "root# sysctl vm.max_launder=-32 ; user$ ./df5815_hog 3400 60 ; watch serial console + try ssh",
  "code_hash": "3182b74e004baa3c537a572affc3a0a86fa953be2f6cc291ff0811e79213ca28",
  "notes": "Guest reset with-src after each wedge run; final state clean. Run #1 was initiated as a control before realizing the -32 setting had not been restored \u2014 its capture is serendipitous but complete (console + observe logs). The same unvalidated-input class applies to several other governor sysctls in this file (vm.pageout_stats_scan, vm.emerg_launder, vm.pageout_stats_inalim); max_launder is the only one whose mis-sign disables a kernel subsystem outright.",
  "recommended_fix": "Clamp vm_max_launder at the use site (negative -> 0) and make the cap conditional; see fix.diff.",
  "fix_status": "not_tested",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (negative clamp + cap-condition fix) applies clean to sys/vm/vm_pageout.c and provably removes the negative-budget path by construction; kernel fix-build not performed (non-corruption Low/DoS finding; guest kernel rebuild ~40min not justified per contract). Baseline (misconfig+wedge) reproduced 2/2 before the fix.",
  "fix_evidence": "findings/poc/DF-5815/fix.diff (git apply --check clean)"
}