Commit b97902b6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'fs.openat2.unknown_flags.v5.14' of...

Merge tag 'fs.openat2.unknown_flags.v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux

Pull openat2 fixes from Christian Brauner:

 - Remove the unused VALID_UPGRADE_FLAGS define we carried from an
   extension to openat2() that we haven't merged. Aleksa might be
   getting back to it at some point but just not right now.

 - openat2() used to accidently ignore unknown flag values in the upper
   32 bits.

   The new openat2() syscall verifies that no unknown O-flag values are
   set and returns an error to userspace if they are while the older
   open syscalls like open() and openat() simply ignore unknown flag
   values:

      #define O_FLAG_CURRENTLY_INVALID (1 << 31)
      struct open_how how = {
            .flags = O_RDONLY | O_FLAG_CURRENTLY_INVALID,
            .resolve = 0,
      };

      /* fails */
      fd = openat2(-EBADF, "/dev/null", &how, sizeof(how));

      /* succeeds */
      fd = openat(-EBADF, "/dev/null", O_RDONLY | O_FLAG_CURRENTLY_INVALID);

   However, openat2() silently truncates the upper 32 bits meaning:

      #define O_FLAG_CURRENTLY_INVALID_LOWER32 (1 << 31)
      #define O_FLAG_CURRENTLY_INVALID_UPPER32 (1 << 40)

      struct open_how how_lowe32 = {
            .flags = O_RDONLY | O_FLAG_CURRENTLY_INVALID_LOWER32,
      };

      struct open_how how_upper32 = {
            .flags = O_RDONLY | O_FLAG_CURRENTLY_INVALID_UPPER32,
      };

      /* fails */
      fd = openat2(-EBADF, "/dev/null", &how_lower32, sizeof(how_lower32));

      /* succeeds */
      fd = openat2(-EBADF, "/dev/null", &how_upper32, sizeof(how_upper32));

   Fix this by preventing the immediate truncation in build_open_flags()
   and add a compile-time check to catch when we add flags in the upper
   32 bit range.

* tag 'fs.openat2.unknown_flags.v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux:
  test: add openat2() test for invalid upper 32 bit flag value
  open: don't silently ignore unknown O-flags in openat2()
  fcntl: remove unused VALID_UPGRADE_FLAGS
parents 30d1a556 15845cbc
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -1002,12 +1002,20 @@ inline struct open_how build_open_how(int flags, umode_t mode)

inline int build_open_flags(const struct open_how *how, struct open_flags *op)
{
	int flags = how->flags;
	u64 flags = how->flags;
	u64 strip = FMODE_NONOTIFY | O_CLOEXEC;
	int lookup_flags = 0;
	int acc_mode = ACC_MODE(flags);

	/* Must never be set by userspace */
	flags &= ~(FMODE_NONOTIFY | O_CLOEXEC);
	BUILD_BUG_ON_MSG(upper_32_bits(VALID_OPEN_FLAGS),
			 "struct open_flags doesn't yet handle flags > 32 bits");

	/*
	 * Strip flags that either shouldn't be set by userspace like
	 * FMODE_NONOTIFY or that aren't relevant in determining struct
	 * open_flags like O_CLOEXEC.
	 */
	flags &= ~strip;

	/*
	 * Older syscalls implicitly clear all of the invalid flags or argument
+0 −4
Original line number Diff line number Diff line
@@ -12,10 +12,6 @@
	 FASYNC	| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE)

/* List of all valid flags for the how->upgrade_mask argument: */
#define VALID_UPGRADE_FLAGS \
	(UPGRADE_NOWRITE | UPGRADE_NOREAD)

/* List of all valid flags for the how->resolve argument: */
#define VALID_RESOLVE_FLAGS \
	(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \
+6 −1
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ struct flag_test {
	int err;
};

#define NUM_OPENAT2_FLAG_TESTS 24
#define NUM_OPENAT2_FLAG_TESTS 25

void test_openat2_flags(void)
{
@@ -229,6 +229,11 @@ void test_openat2_flags(void)
		{ .name = "invalid how.resolve and O_PATH",
		  .how.flags = O_PATH,
		  .how.resolve = 0x1337, .err = -EINVAL },

		/* currently unknown upper 32 bit rejected. */
		{ .name = "currently unknown bit (1 << 63)",
		  .how.flags = O_RDONLY | (1ULL << 63),
		  .how.resolve = 0, .err = -EINVAL },
	};

	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);