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

dirfs_mount copyinstr fallback re-interprets a failed user data pointer as a kernel address β€” wild vkernel-kernel read / fatal trap from mount(2)

Summary

When copyinstr(data,...) fails (EFAULT on a bad user pointer), dirfs_mount retries with copystr(data,...) treating the same user-supplied value as a kernel pointer. The vkernel copystr (vkernel64/platform/copyio.c:251-263) is an unprotected byte loop: unmapped address -> fatal kernel-mode fault; a mapped one copies up to 1023 bytes of arbitrary vkernel-kernel memory into dm_path (bounded write, indirectly observable read primitive). No in-tree caller passes a kernel data pointer - the vkernel root mount passes data=NULL which takes the same crash path - so the fallback serves nobody. PROVEN on guest vkernel: mount('dirfs','/hostroot',0,(void*)0x1000) -> 'Fatal trap 12 ... Stopped at copystr+0x5 (movzbl (%rdi),%eax)' - the loop's first load of the attacker-chosen address. Fix validated: fallback removed -> clean EFAULT, vkernel alive. Requires mount privilege (vkernel root/fscap/usermount); multi-tenant vkernel = cross-tenant crash.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3222 Β· 11 files
FileTypeDescriptionSize
run.log β€” 2.8 KB view raw
run.fixed.log β€” 2.8 KB view raw
run.sh β€” 157 B view raw
run-fixed.sh β€” 1.2 KB view raw
vkmount2.c β€” 536 B view raw
fix.diff β€” 514 B view raw
fix-build.log β€” 10.0 KB view raw
build.fixed-kernel.log β€” 10.0 KB view raw
VERDICT.md β€” 2.4 KB ↓ raw
manifest.json β€” 775 B view raw
verdict.json β€” 3.1 KB view raw
VERDICT.md
↓ download raw

DF-3222 VERDICT

status: reproduced / impact: panic (vkernel DoS from a mount-privileged user) / confidence: certain

Environment

Same harness as DF-3218: vkernel (VKDIRFS) built and run inside the QEMU guest, minimal FFS root, fifo console. Guest uname in ../DF-3218/env.txt.

Chain

/bin/vkmount2 /hostroot inside the vkernel calls mount("dirfs", "/hostroot", 0, (void *)0x1000). domount (sys/kern/vfs_syscalls.c:416) passes uap->data through; dirfs_mount copyinstr(0x1000) β†’ EFAULT β†’ fallback copystr(0x1000, dm_path, MAXPATHLEN, &done) (dirfs_vfsops.c:129) treats the user value as a kernel pointer; vkernel copystr (copyio.c:255-260) loads ((const char *)0x1000)[0] β†’ fatal kernel-mode page fault.

Observed

Baseline (run.log):

Fatal trap 12: page fault while in kernel mode
current process = 2 (vkmount2)
Stopped at 0x726b85: movzbl (%rdi),%eax

Symbolized: copystr+0x5 (nm: copystr at 0x726b80; objdump shows the faulting instruction is the loop's first movzbl (%rdi),%eax load). The vkernel panics to ddb; the vkernel OS is dead.

Fixed (run.fixed.log + fix.diff β€” fallback removed):

mount failed: Bad address
# echo STILL-ALIVE
STILL-ALIVE

copyinstr's EFAULT propagates as a clean mount(2) error; vkernel alive. fix_status: fixed.

Why Low severity

Requires mount privilege (vkernel root, dirfs fscap holder, or vfs.usermount=1 + mountpoint ownership β€” the caps gate is sys/kern/vfs_syscalls.c:152-172). For the single-tenant vkernel the crasher owns the process anyway; the security-relevant variant is the capability-granted multi-tenant vkernel (tenant crashes the kernel for all users), or the mapped-address variant (bounded arbitrary vkernel-kernel read into dm_path, only indirectly observable). No memory write outside the bounded dm_path destination.

Note

The vkernel root-mount path (vfs_conf.c:457 VFS_MOUNT(mp, NULL, NULL, ...)) passes data=NULL, so even the "kernel mount" case the comment claims to serve would take this same crash path on a dirfs root mount β€” the fallback serves no in-tree caller.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Fallback removed, vkernel relinked, identical PoC re-run: copyinstr EFAULT returns as 'mount failed: Bad address'; vkernel alive and shell responsive (run.fixed.log).

['run.fixed.log', 'fix-build.log', 'build.fixed-kernel.log', 'fix.diff']
↓ fix.diffvkernel VKDIRFS (DragonFly 6.5-DEVELOPMENT, guest scratch tree with fix.diff)

Confirmed kernel references

Detail

Exploit chain

mount-privileged user in vkernel calls mount("dirfs", dir, 0, (void*)0x1000) -> domount passes uap->data -> copyinstr(0x1000) EFAULT -> copystr(0x1000,...) as kernel pointer -> first byte load faults -> Fatal trap 12, whole-vkernel panic. Mapped-address variant: bounded read of up to 1023 bytes of vkernel-kernel memory into dm_path (indirectly observable only); no out-of-bounds write.

Evidence (decisive lines)

["run.log: 'Fatal trap 12 ... current process = 2 (vkmount2)', 'Stopped at 0x726b85: movzbl (%rdi),%eax'", 'VERDICT.md: 0x726b85 symbolized as copystr+0x5 (vkernel copyio.c:255 loop head)', "run.fixed.log: 'mount failed: Bad address' + 'STILL-ALIVE' β€” vkernel alive with fix.diff"]

PoC changes

vkmount2.c written fresh (mount(2) with data=(void*)0x1000); harness identical to DF-3218's (custom vkernel build after bit-rot include fixes, FFS root image, vkinit wrapper, fifo console).

Verified recommended fix

Delete the copystr fallback; fail the mount on copyinstr error (see fix.diff). If a kernel-internal mount API ever needs dirfs, it should pass the path through a UIO_SYSSPACE-aware interface, not by guessing pointer provenance.

Verdict

dirfs_mount's copyinstr fallback re-interprets a failed user data pointer as a kernel address and copystr()s from it (dirfs_vfsops.c:129); the vkernel copystr has no fault handling, so a mount(2) with data=(void*)0x1008 crashes the vkernel in kernel mode. Verified on the guest: Fatal trap 12 in copystr+0x5 (movzbl (%rdi),%eax), current process vkmount2. Requires mount privilege (vkernel root / dirfs fscap / vfs.usermount=1), hence Low. fix.diff (remove the fallback) validated: mount fails cleanly with EFAULT (Bad address) and the vkernel survives.