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

Non-root tmpfs mounts take attacker-supplied root uid/gid (only mode is sanitized): forged directory ownership plus statfs f_owner overwrite breaks the unmount-ownership authorization (mounter lockout / arbitrary-uid unmount grant)

Summary

tmpfs_mount copies ta_root_uid/ta_root_gid verbatim from the fully user-controlled tmpfs_mount_info (:142-144) and sanitizes only root_mode for non-root mounters (:151-155). Consequences: (a) a user-mounted tmpfs root directory reports arbitrary ownership (VERIFIED uid=0 gid=0 from uid 1001); (b) tmpfs_statfs overwrites f_owner with tm_root->tn_uid (:494), and sys_unmount authorizes by f_owner == cred->cr_uid || priv (vfs_syscalls.c:670) - VERIFIED: the real mounter's unmount now fails EPERM (self-lockout), symmetrically the forged uid is granted unmount/update rights on the attacker's mount. Unpriv user (vfs.usermount=1) forges audit-visible ownership of a live filesystem and manipulates who may unmount it; modest ceiling (no direct privilege gain) but defeats the kernel's mount-ownership model. Fix: root_uid/root_gid = cred's for non-root mounters + stop overwriting f_owner in statfs.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3183 Β· 9 files
FileTypeDescriptionSize
README.md β€” 1.6 KB ↓ raw
VERDICT.md β€” 2.2 KB ↓ raw
build.log β€” 65 B view raw
build.sh β€” 68 B view raw
env.txt β€” 419 B view raw
run.log β€” 1.1 KB view raw
run.sh β€” 91 B view raw
umount_demo.c β€” 3.3 KB view raw
verdict.json β€” 2.1 KB view raw

DF-3183 β€” non-root tmpfs mounts take attacker-supplied root uid/gid (mode is sanitized, ownership is not) β†’ statfs f_owner forgery + unmount-permission confusion

What

tmpfs_mount (sys/vfs/tmpfs/tmpfs_vfsops.c:142-155) copies ta_root_uid / ta_root_gid from the (fully user-controlled) struct tmpfs_mount_info and only sanitizes root_mode for non-root mounters:

    root_uid = args.ta_root_uid;        /* :142 β€” NOT sanitized below */
    root_gid = args.ta_root_gid;        /* :143 */
    root_mode = args.ta_root_mode;
    ...
    if (cred->cr_uid != 0) {
        root_mode = VREAD;          /* only mode is forced */
        ...
    }

Consequences for a vfs.usermount=1 unprivileged mount: * the tmpfs root directory is owned by any chosen uid/gid (e.g. 0); * tmpfs_statfs overwrites f_owner with tm_root->tn_uid (tmpfs_vfsops.c:494), and sys_unmount authorizes unmount by mp->mnt_stat.f_owner == cred->cr_uid || priv (sys/kern/vfs_syscalls.c:670) β€” so the forged uid can unmount the attacker's mount, and the real mounter is locked out.

Observed (guest, user maxx uid 1001, vfs.usermount=1)

mounter uid=1001 euid=1001, spoofing ta_root_uid/gid=0, ta_size_max=1TiB
statfs: f_blocks=268435456 ..., f_owner=0 (mounter is 1001)
root dir owner: uid=0 gid=0 mode=40600
unmount by mounter: FAILED (Operation not permitted) β€” f_owner forged to 0

Control run without the spoof unmounts fine (unmount by mounter: OK).

Fix

For non-root mounters force root_uid = cred->cr_uid; root_gid = cred->cr_gid; alongside the existing root_mode override, and stop overwriting f_owner in tmpfs_statfs (leave the mounter's uid recorded by the VFS layer).

VERDICT.md
↓ download raw

DF-3183 verdict β€” REPRODUCED (non-root tmpfs mount forges root-dir uid/gid and statfs f_owner; unmount-permission confusion)

Guest: DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC (INVARIANTS), vfs.usermount=1, unprivileged user maxx (uid 1001).

Observed (run.log):

mounter uid=1001 euid=1001, spoofing ta_root_uid/gid=0, ta_size_max=1TiB statfs: f_blocks=268435456 ..., f_owner=0 (mounter is 1001) root dir owner: uid=0 gid=0 mode=40600 unmount by mounter: FAILED (Operation not permitted) β€” f_owner forged to 0

Control (no spoof): f_owner=1001, root dir uid=1001, unmount OK.

Source: tmpfs_mount copies args.ta_root_uid/ta_root_gid verbatim (tmpfs_vfsops.c:142-144) and sanitizes only root_mode for non-root (:151-155). tmpfs_statfs then publishes tm_root->tn_uid as f_owner (:494), and sys_unmount authorizes by mp->mnt_stat.f_owner == cred->cr_uid || priv (kern/vfs_syscalls.c:670). Net effects: 1. forged f_owner β†’ the named uid can unmount the attacker's mount (or mount -u update check at vfs_syscalls.c:263), while the real mounter is locked out (demonstrated: EPERM self-lockout); 2. the root directory of a user-mounted tmpfs reports arbitrary ownership (uid=0 gid=0 demonstrated) β€” auditing/ownership confusion (files created within still belong to the creating users).

Impact ceiling is modest (no direct privilege gain β€” permission checks on the spoofed-owned directory do not help the non-owner mounter; mode is forced to 0600). Filed Low: ownership/f_owner forgery on a usermountable filesystem plus unmount authorization confusion.

Status: reproduced / impact=none (privilege-relevant confusion only) / confidence certain.

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean
[]
per-fix-DF-3183

Confirmed kernel references

Detail

Evidence (decisive lines)

['run.log (spoof + control)', 'umount_demo.c', 'VERDICT.md']

PoC changes

same binary as DF-3182 (spoof uid argument)

Verified recommended fix

force root_uid/root_gid to cred values for non-root mounters; stop overwriting f_owner in tmpfs_statfs

Verdict

REPRODUCED (forgery): unprivileged mount (vfs.usermount=1) with ta_root_uid=0/ta_root_gid=0 produced a tmpfs whose root directory reports uid=0 gid=0 (mode correctly forced to 0600) and whose statfs f_owner is 0 while the mounter is uid 1001; the mounter's own unmount then fails EPERM (sys_unmount authorizes via mnt_stat.f_owner at vfs_syscalls.c:670, which tmpfs_statfs overwrote with tm_root->tn_uid at tmpfs_vfsops.c:494). Control run without spoof: f_owner=1001 and unmount OK. tmpfs_mount copies ta_root_uid/ta_root_gid verbatim (tmpfs_vfsops.c:142-144) and sanitizes only root_mode (:151-155). Impact ceiling modest: unmount-authorization confusion (forged uid may unmount/update the attacker's mount; real mounter locked out β€” demonstrated) and ownership spoofing; no direct privilege gain found.