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

Daemon LOOKUP reply with S_IFMT-clear mode yields vtyp=VBAD β†’ fuse_node_vn KKASSERT(0) panic (INVARIANTS force-enabled module-wide)

Field Value
ID DF-3028
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-20 / CWE-617
File sys/vfs/fuse/fuse_vnops.c
Lines 554-569 (sinks: fuse_node.c:103-104, :210-211; fuse.h:31-33)
Area vfs/fuse
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:vfs
Reported pending
Known CVE none
CVE match novel

Summary

fuse_vnops.c maps a daemon-supplied mode with no S_IFMT bits to vtyp=VBAD with no validation; fuse_alloc_node rejects only VBLK/VCHR/VFIFO, so the VBAD node reaches fuse_node_vn whose switch default is KKASSERT(0). fuse.h force-defines INVARIANTS for the entire module, so the panic fires on ANY kernel configuration. One crafted reply + any name resolution = guaranteed panic. Malicious/compromised FUSE daemon (or any process that received the /dev/fuse fd via SCM_RIGHTS) β€” the stated trust boundary; one reply permanently DoSes the host, and the resolving syscall (stat/open/ls) can be issued by any unprivileged user. Distinct from DF-0926 (existing-nodeid type confusion β†’ fuse_set_attr KKASSERT): this is a new nodeid with garbage S_IFMT hitting a different sink. VERIFIED live (panic.txt with the exact predicted backtrace); patched module returns EINVAL.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of fuse_vnops.c (GLM 5.3); daemon-crafted-reply panic reproduced + fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3028 Β· 9 files
FileTypeDescriptionSize
fuse_daemon.c β€” 16.5 KB view raw
vbad_trigger.c β€” 993 B view raw
build.sh β€” 132 B view raw
run.sh β€” 576 B view raw
panic.txt β€” 634 B view raw
fix.diff β€” 808 B view raw
fix_run.log β€” 705 B view raw
VERDICT.md β€” 1.6 KB ↓ raw
env.txt β€” 203 B view raw
VERDICT.md
↓ download raw

DF-3028 verdict β€” REPRODUCED (deterministic panic, malicious-daemon DoS)

Root cause (path:line)

Reproduction (guest DragonFly 6.5-DEVELOPMENT #0)

Daemon mode vbad-lookup (single LOOKUP reply with mode bits = 0644, i.e. S_IFMT clear). Trigger: unprivileged stat /mnt/fuse/target. Result (panic.txt):

panic: assertion "0" failed in fuse_node_vn at /usr/src/sys/vfs/fuse/fuse_node.c:211
fuse_node_vn() at fuse_node_vn+0x3b8
fuse_alloc_node() at fuse_alloc_node+0x110
fuse_vop_nresolve() at fuse_vop_nresolve+0x246
vop_nresolve() ... cache_resolve()

Guest dropped into DDB; ssh died. One daemon reply = one guaranteed panic.

Trust model

The FUSE daemon is a trust boundary (a compromised/malicious userspace FS, or any process that obtained the /dev/fuse fd via SCM_RIGHTS). This is a single-reply, persistent-mount remote-equivalent DoS from that boundary; the trigger itself runs unprivileged.

Fix validation

fix.diff (reject VBAD/VNON in nresolve before alloc + harden fuse_alloc_node) applied in-guest, module rebuilt: identical trigger returns EINVAL (stat fails cleanly), guest stays up (fix_run.log). fix_status=fixed.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched module: identical trigger returns EINVAL (stat fails cleanly), no panic, guest healthy.

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

malicious/compromised daemon (or any process holding the /dev/fuse fd) sends one crafted LOOKUP reply -> any name resolution on the mount (stat/open/ls by any user) panics the kernel; repeatable after reboot, persistent DoS of the host

Evidence (decisive lines)

['panic.txt: assertion "0" failed in fuse_node_vn at fuse_node.c:211 via fuse_alloc_node <- fuse_vop_nresolve <- cache_resolve', 'fix_run.log: patched stat failed: Invalid argument (no panic), guest alive']

PoC changes

derived from DF-0780 daemon harness; added vbad-lookup mode (LOOKUP reply mode=0644) and namespace support so mount/stat flow works

Verified recommended fix

Reject VBAD/VNON vtyp in fuse_vop_nresolve (return EINVAL) and harden fuse_alloc_node to reject them as well.

Verdict

A daemon LOOKUP reply with S_IFMT-clear attr.mode yields vtyp=VBAD in fuse_vop_nresolve (fuse_vnops.c:568); fuse_alloc_node passes VBAD through (fuse_node.c:103 rejects only VBLK/VCHR/VFIFO) and fuse_node_vn's switch default KKASSERT(0) (fuse_node.c:211) panics. fuse.h:31-33 force-enables INVARIANTS module-wide so this fires on any kernel. Verified: unprivileged stat() on a mount served by a vbad-lookup daemon panicked the guest instantly with the exact predicted backtrace; patched module returns EINVAL and survives.