Page MenuHomeFreeBSD

vfs: Don't clobber namei flags in vn_open_cred()
ClosedPublic

Authored by markj on May 26 2025, 1:57 PM.
Tags
None
Referenced Files
F132388667: D50531.id156049.diff
Thu, Oct 16, 11:58 AM
Unknown Object (File)
Wed, Oct 8, 11:29 PM
Unknown Object (File)
Mon, Oct 6, 5:40 AM
Unknown Object (File)
Thu, Oct 2, 11:22 PM
Unknown Object (File)
Wed, Oct 1, 8:22 AM
Unknown Object (File)
Sun, Sep 28, 9:36 AM
Unknown Object (File)
Sep 12 2025, 5:20 AM
Unknown Object (File)
Sep 4 2025, 1:02 PM
Subscribers

Details

Summary

Otherwise NAMEILOOKUP is cleared. More generally it seems quite
surprising that the flags set by vn_open_cred() callers are not
automatically preserved. I see at least one bug due to this:
zfs_getextattr_dir()'s use of NOFOLLOW is ignored, as it doesn't also
specify O_NOFOLLOW.

After going through callers, I believe it's safe to add the flags this
way.

Fixes: 7587f6d4840f ("namei: Make stackable filesystems check harder for jail roots")

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

markj requested review of this revision.May 26 2025, 1:57 PM

Fix up handling of O_NOFOLLOW.

sys/kern/vfs_vnops.c
190

I suggest changing the open2nameif() signature to

static void open2nameif(int fmode, u_int vn_open_flags, struct nameidata *ndp);

and having the 'or' hidden in the function.

Browsing some old notes, I had noticed that cn_flags were sometimes crushed, and concluded that VN_* flags probably were just workarounds for that problem, except for VN_OPEN_INVFS.

Now, not crushing some flags on O_CREAT might introduce subtle semantics change. An audit of all direct vn_open_cred() callers does not show any problem (but make me notice a bug that the change here will fix as a side-effect, see below). However, I have not audited callers of vn_open().

The bug you noticed in zfs_setextattr_dir() about NOFOLLOW being ignored is in fact only a conceptual one, as NOFOLLOW is in fact 0. However, it seems there is a real, similar bug in openat() but with FOLLOW.

sys/kern/vfs_vnops.c
190

I'm OK with open2nameif() doing the OR (and actually more, see the other inline comment), but not really with passing ndp, which obfuscates at calling points that this function is only meant to compute cn_flags.

I'd rather suggest passing a pointer to cn_flags instead of ndp.

358–360

I would put the O_FOLLOW handling into open2nameif().

markj marked an inline comment as done.

Fix FOLLOW/NOFOLLOW handling.

Browsing some old notes, I had noticed that cn_flags were sometimes crushed, and concluded that VN_* flags probably were just workarounds for that problem, except for VN_OPEN_INVFS.

Now, not crushing some flags on O_CREAT might introduce subtle semantics change. An audit of all direct vn_open_cred() callers does not show any problem (but make me notice a bug that the change here will fix as a side-effect, see below). However, I have not audited callers of vn_open().

The bug you noticed in zfs_setextattr_dir() about NOFOLLOW being ignored is in fact only a conceptual one, as NOFOLLOW is in fact 0. However, it seems there is a real, similar bug in openat() but with FOLLOW.

openatfp() ORs FOLLOW into the cnflags unless O_NOFOLLOW is set, and zfs_setextattr_dir() does not specify O_NOFOLLOW. So its use of NOFOLLOW is silently getting converted to FOLLOW. Maybe it's not a real problem in practice, but it doesn't matter that NOFOLLOW is 0.

sys/kern/vfs_vnops.c
190

I did it a bit differently than both suggestions, such that open2nameif() continues to be a pure function.

sys/kern/vfs_vnops.c
279

IMO this is too convoluted. open2namei() already set FOLLOW in !O_NOFOLLOW. So to keep the existing behavior, isn't it enough to clear FOLLOW if O_EXCL is set, without two lines above that set FOLLOW?

  • Simplify O_EXCL handling.
  • Preserve NOFOLLOW from callers, inadvertently lost in the previous revision.

Now, a caller of vn_open_cred() has to explicitly specify FOLLOW, else it gets NOFOLLOW
semantics. O_FOLLOW overrides FOLLOW.

This revision is now accepted and ready to land.May 27 2025, 7:43 PM

openatfp() ORs FOLLOW into the cnflags unless O_NOFOLLOW is set, and zfs_setextattr_dir() does not specify O_NOFOLLOW. So its use of NOFOLLOW is silently getting converted to FOLLOW. Maybe it's not a real problem in practice, but it doesn't matter that NOFOLLOW is 0.

You probably meant vn_open_cred() here. Yes, I forgot about the special handling of O_NOFOLLOW after calling open2nameif(), with indeed causes a real problem in zfs_setextattr_dir().

O_FOLLOW overrides FOLLOW.

O_NOFOLLOW, yes. Doing so seems quite reasonable.