โฌข DragonFlyBSD Kernel Audit
DF-2801 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-2801",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED: failed jail(2) calls permanently pin namecache entries. kern_jail() cache_copy()s the jail path handle into pr->pr_root (kern_jail.c:206) and no error path after that drops it (assign_prison_id :211-216, prison_sysctl_create, kern_jail_attach/out2 :227-244; sys_jail kfrees pr at :342-351 with the reference live). Trigger: jail path = regular file โ€” nlookup succeeds, cache_copy runs, kern_chroot/checkvp_chdir fails ENOTDIR (vfs_syscalls.c:2058). Measured with a floor-comparison design (2000 files per cycle, 8000-file pressure churn + settle between phases to defeat lazy hammer2 reclaim): stock kernel control cycle -104 vs leak cycle +1538 permanently-pinned entries for 2000 calls (all 2000 failing ENOTDIR, i.e. exactly the traced path). Patched kernel (cache_drop on every error path, NULL-guarded): leak growth collapses to +118, same order as control noise, with the identical ENOTDIR failure count. Root-gated (jail(2) needs SYSCAP_NOJAIL_CREATE) so this is a privileged-user unbounded-kernel-memory-exhaustion primitive, not an unpriv escalation โ€” rated Low severity, impact ceiling DoS.",
  "exploit_chain": "host root: loop { jail(path=<regular file>) } โ€” each failing call leaks one namecache reference forever; unbounded kernel memory growth. No unprivileged route (jail(2) is capability-gated and denied in jails).",
  "evidence": [
    "findings/poc/DF-2801/run.3.log โ€” decisive stock run: F0 6476 / control -104 / leak +1538, ENOTDIR=2000, REPRODUCED line",
    "findings/poc/DF-2801/run.patched.log โ€” fixed: control -91, leak +118 (noise), ENOTDIR=2000 same path"
  ],
  "kernel_refs": [
    "sys/kern/kern_jail.c:206", "sys/kern/kern_jail.c:211", "sys/kern/kern_jail.c:223",
    "sys/kern/kern_jail.c:227", "sys/kern/kern_jail.c:234", "sys/kern/kern_jail.c:342",
    "sys/kern/vfs_syscalls.c:2058", "sys/kern/vfs_cache.c:751"
  ],
  "poc_changes": "two measurement fixes: (1) vfs.cache.numcache is an INT sysctl โ€” first version parsed it as a string and read 0; (2) baseline must be compared as settled floors with reclaim pressure between phases โ€” instantaneous deltas are confounded by lazy hammer2 reclaim (first design took the baseline after file creation and showed delta 0).",
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -o jailleak jailleak.c",
  "run_cmd": "sh run.sh   (root; leak cycle + control cycle with pressure/settle floors)",
  "code_hash": "ffe400b143d020207ae8a3630d69a207b828b5bcb1e5dbe7889125dae0090b94",
  "notes": "cache_copy also runs on the SUCCESS path where the reference is correctly dropped by prison_free (:837) โ€” only the error paths leak. Fixed kernel = #1 Tue Sep 1 13:39:40 UTC 2026.",
  "recommended_fix": "Add NULL-guarded cache_drop(&pr->pr_root) to every kern_jail error return after cache_copy and to sys_jail's out path before kfree(pr).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep  1 13:39:40 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel: identical trigger (2000 ENOTDIR failures) pins nothing โ€” leak-cycle floor growth +118 vs control -91 (noise), against +1538 on stock. Permanent pinning eliminated.",
  "fix_evidence": "findings/poc/DF-2801/run.patched.log; fix.diff (kern_jail.c cache_drop hunks)"
}