{
  "finding_id": "DF-3228",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED on the stock guest: fuse_cdevpriv_dtor (fuse_device.c:38-45) drops the open-time reference only when fuse_cdevpriv_close() returns 0; for a never-mounted fd it returns ENODEV (fuse_device.c:84-88) and the dtor skips fuse_mount_free() \u2014 every open(/dev/fuse)+close() without a mount leaks sizeof(struct fuse_mount). Measured: vmstat -m temp grew 472 allocs/335K -> 46.1K allocs/20.3MB within ~5 min of a 100k-cycle loop (still running when sampled), linear and unbounded. Semi-privileged primitive: /dev/fuse is 0660 root:operator (fuse_device.c:313-4), so group operator / root; slow kernel-heap exhaustion (CWE-401). devfs_set_cdevpriv()s return is also ignored at fuse_device.c:57.",
  "exploit_chain": "operator/root account: loop open+close on /dev/fuse without mounting -> ~256B M_TEMP leak per cycle -> unbounded kernel heap exhaustion -> memory-pressure DoS for all kernel subsystems.",
  "evidence": [
    "findings/poc/DF-3228/run.log \u2014 baseline temp 472/335K -> 46.1K/20.3MB after 100k cycles",
    "findings/poc/DF-3228/fix_run2.log \u2014 patched: 20000 cycles, temp identical 472/335K before/after, no panic",
    "findings/poc/DF-3228/fix_run.log \u2014 fix v1 regression proof: plain fuse_mount_free() on never-mounted fmp panics in crfree(NULL) (Fatal trap 12 crfree+0xc) \u2014 why fix v2 uses plain kfree"
  ],
  "kernel_refs": [
    "sys/vfs/fuse/fuse_device.c:38-45",
    "sys/vfs/fuse/fuse_device.c:53-57",
    "sys/vfs/fuse/fuse_device.c:82-97",
    "sys/vfs/fuse/fuse_vfsops.c:78-93",
    "sys/vfs/fuse/fuse_device.c:313-314"
  ],
  "poc_changes": "none \u2014 trivial loop written fresh; format warnings (int vs long) in the printf summary are cosmetic and do not affect the leak measurement.",
  "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": 600,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o /tmp/df3228 /tmp/poc-src/DF-3228/df3228.c",
  "run_cmd": "kldload fuse && vmstat -m | grep temp && /tmp/df3228 100000 && vmstat -m | grep temp",
  "code_hash": "fee0405af21196a5a069dbaab79553c81051e603dcf92c960127da86732dee36",
  "notes": "Fix iteration documented honestly: v1 (always fuse_mount_free) introduced a crfree(NULL) panic because fmp->cred is only assigned in fuse_mount() (fuse_vfsops.c:185) \u2014 caught by the fix-validation run; v2 kfree()s the never-mounted object (refcnt==1, no mtx_inited locks, no cred) and checks devfs_set_cdevpriv() in open. v2 validated: zero growth over 20000 cycles, no panic.",
  "recommended_fix": "fuse_cdevpriv_dtor: if (fmp->devvp) { fuse_cdevpriv_close(fmp); fuse_mount_free(fmp); } else kfree(fmp, M_TEMP); plus check devfs_set_cdevpriv() return in fuse_device_open (see fix.diff).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 (fuse.ko module rebuilt in-guest with fix.diff v2)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched fuse.ko: 20000 open+close cycles leave vmstat -m temp byte-identical to baseline (472 allocs/335K) with no panic; baseline leaked ~256B/cycle.",
  "fix_evidence": [
    "findings/poc/DF-3228/fix_run2.log",
    "findings/poc/DF-3228/fix.diff"
  ]
}