β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3030

fuse_vop_nrename leaks a kmalloc'd M_TEMP buffer on every effective rename β€” unprivileged unbounded kernel heap exhaustion

Field Value
ID DF-3030
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H
CWE CWE-401
File sys/vfs/fuse/fuse_vnops.c
Lines 1219-1227
Area vfs/fuse
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

fuse_vop_nrename kmallocs newname (tncp->nc_nlen+1, M_TEMP, M_WAITOK|M_ZERO) whenever the new name differs, uses it ONLY inside fuse_dbg(), and never frees it on any exit path. Each rename(2) permanently leaks ~name-length bytes. VERIFIED: 100,000 unprivileged renames of ~264-char names leaked 19.4MB (474 β†’ 98.1K M_TEMP allocations) in 4.6s; +40k renames β†’ 27.0MB (linear, unbounded). Patched module: zero growth. Any unprivileged user with write permission on any FUSE mount (usermount, or a world/group-writable directory on a system fuse mount) loops renames to exhaust the kernel memory reserve β€” sustained ~4MB/s per process, trivially parallelized, ending in system-wide allocation failure/panic; mount survives, leak permanent. Fix: kfree after the fuse_dbg (row diff).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of fuse_vnops.c (GLM 5.3); leak measured linear + fix validated on rebuilt module.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3030 Β· 9 files
FileTypeDescriptionSize
fuse_daemon.c β€” 16.5 KB view raw
rename_leak.c β€” 2.0 KB view raw
build.sh β€” 130 B view raw
run.sh β€” 788 B view raw
run.log β€” 392 B view raw
fix.diff β€” 315 B view raw
fix_run.log β€” 702 B view raw
VERDICT.md β€” 1.3 KB ↓ raw
env.txt β€” 203 B view raw
VERDICT.md
↓ download raw

DF-3030 verdict β€” REPRODUCED (unbounded kernel heap exhaustion, unprivileged)

Root cause (path:line)

sys/vfs/fuse/fuse_vnops.c:1219-1227:

newname = kmalloc(tncp->nc_nlen + 1, M_TEMP, M_WAITOK | M_ZERO);
KKASSERT(newname);
memcpy(newname, tncp->nc_name, tncp->nc_nlen);
newname[tncp->nc_nlen] = '\0';
fuse_dbg("newname=\"%s\"\n", newname);   <-- only use

No kfree(newname) anywhere in the function (verified through the out: label). The allocation happens on every rename whose target name differs from the source name.

Reproduction (guest DragonFly 6.5-DEVELOPMENT #0)

BEFORE:  temp   474   336K      (vmstat -m)
su -m maxx -c "./rename_leak /mnt/fuse 50000"    # 100000 renames, 4.57s
AFTER:   temp  98.1K  19.4M
+ 20000 more iterations (40000 renames):
AFTER2:  temp   137K  27.0M

Linear, unbounded, no ceiling: an unprivileged user loops renames (the PoC sustains ~22k renames/s; multiple processes multiply this) until the 390M kmem pool is exhausted -> allocation failures/panic system-wide.

Fix validation

fix.diff adds kfree(newname, M_TEMP) right after the debug use (the allocation itself is only needed by fuse_dbg; kept for minimal diff). Patched module, identical 100000-renames run: M_TEMP 474 -> 474, 336K in use before AND after. fix_status=fixed.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched module, identical 100000-renames run: M_TEMP unchanged (474 allocs, 336K in use).

fix_run.log, fix.diff
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #0 (fuse.ko rebuilt with fix.diff in-guest)

Confirmed kernel references

Detail

Exploit chain

unprivileged user with write permission on any FUSE mount loops rename(2) between two long names (~22k/s per process in the PoC; trivially parallel) -> kernel heap exhaustion -> system-wide allocation failure/panic

Evidence (decisive lines)

['run.log: temp 474/336K -> 98.1K/19.4M after 100k renames, -> 137K/27.0M after 140k', 'fix_run.log: patched 474 -> 474 after 100k renames']

PoC changes

daemon extended with a real synthetic namespace (LOOKUP/CREATE/RENAME/UNLINK bookkeeping; fuse_create_in is 16 bytes incl umask+padding - an 8-byte mirage broke name parsing) and 0777 root dir so unpriv create/rename works

Verified recommended fix

kfree(newname, M_TEMP) after its only use (or drop the allocation and print from tncp->nc_name directly).

Verdict

fuse_vop_nrename kmallocs newname (M_TEMP) on every effective rename and never frees it (only used by fuse_dbg). Verified: 100000 unprivileged renames of ~264-char names permanently leaked 19.4MB of kernel M_TEMP (474->98.1K allocations) in 4.6 seconds; +40000 renames -> 27.0MB, linear and unbounded, an unprivileged user can exhaust kernel memory. Patched module shows zero growth under the identical load.