linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] openat2: flag fixes
@ 2021-05-28  9:24 Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 1/3] fcntl: remove unused VALID_UPGRADE_FLAGS Christian Brauner
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Christian Brauner @ 2021-05-28  9:24 UTC (permalink / raw)
  To: Christoph Hellwig, Al Viro
  Cc: Aleksa Sarai, Richard Guy Briggs, linux-fsdevel, Christian Brauner

From: Christian Brauner <christian.brauner@ubuntu.com>

Hey,

A few fixes and tests to openat2 to prevent silent truncation when
passing in flags in the upper 32 bits.
With the feedback from Richard worked in I picked this up so it can sit
in -next for a bit.

Thanks!
Christian

Christian Brauner (3):
  fcntl: remove unused VALID_UPGRADE_FLAGS
  open: don't silently ignore unknown O-flags in openat2()
  test: add openat2() test for invalid upper 32 bit flag value

 fs/open.c                                      | 14 +++++++++++---
 include/linux/fcntl.h                          |  4 ----
 tools/testing/selftests/openat2/openat2_test.c |  7 ++++++-
 3 files changed, 17 insertions(+), 8 deletions(-)


base-commit: c4681547bcce777daf576925a966ffa824edd09d
-- 
2.27.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/3] fcntl: remove unused VALID_UPGRADE_FLAGS
  2021-05-28  9:24 [PATCH v2 0/3] openat2: flag fixes Christian Brauner
@ 2021-05-28  9:24 ` Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2() Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value Christian Brauner
  2 siblings, 0 replies; 6+ messages in thread
From: Christian Brauner @ 2021-05-28  9:24 UTC (permalink / raw)
  To: Christoph Hellwig, Al Viro
  Cc: Aleksa Sarai, Richard Guy Briggs, linux-fsdevel, Christian Brauner

From: Christian Brauner <christian.brauner@ubuntu.com>

We currently do not maky use of this feature and should we implement
something like this in the future it's trivial to add it back.

Cc: Christoph Hellwig <hch@lst.de>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Suggested-by: Richard Guy Briggs <rgb@redhat.com>
Reviewed-by: Richard Guy Briggs <rgb@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
/* v2 */
unchanged
---
 include/linux/fcntl.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 766fcd973beb..a332e79b3207 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -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 | \
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2()
  2021-05-28  9:24 [PATCH v2 0/3] openat2: flag fixes Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 1/3] fcntl: remove unused VALID_UPGRADE_FLAGS Christian Brauner
@ 2021-05-28  9:24 ` Christian Brauner
  2021-05-28 14:19   ` Richard Guy Briggs
  2021-05-28  9:24 ` [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value Christian Brauner
  2 siblings, 1 reply; 6+ messages in thread
From: Christian Brauner @ 2021-05-28  9:24 UTC (permalink / raw)
  To: Christoph Hellwig, Al Viro
  Cc: Aleksa Sarai, Richard Guy Briggs, linux-fsdevel, Christian Brauner

From: Christian Brauner <christian.brauner@ubuntu.com>

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().

There's a snafu here though stripping FMODE_* directly from flags would
cause the upper 32 bits to be truncated as well due to integer promotion
rules since FMODE_* is unsigned int, O_* are signed ints (yuck).

In addition, struct open_flags currently defines flags to be 32 bit
which is reasonable. If we simply were to bump it to 64 bit we would
need to change a lot of code preemptively which doesn't seem worth it.
So simply add a compile-time check verifying that all currently known
O_* flags are within the 32 bit range and fail to build if they aren't
anymore.

This change shouldn't regress old open syscalls since they silently
truncate any unknown values anyway. It is a tiny semantic change for
openat2() but it is very unlikely people pass ing > 32 bit unknown flags
and the syscall is relatively new too.

Cc: Christoph Hellwig <hch@lst.de>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Reported-by: Richard Guy Briggs <rgb@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Aleksa Sarai <cyphar@cyphar.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
/* v2 */
- Richard Guy Briggs <rgb@redhat.com>:
  - Add an explicit BUILD_BUG_ON() to check when we need to change
    struct open_flags to account for O_* flags > 32 bits.
---
 fs/open.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index e53af13b5835..53bc0573c0ec 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -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
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value
  2021-05-28  9:24 [PATCH v2 0/3] openat2: flag fixes Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 1/3] fcntl: remove unused VALID_UPGRADE_FLAGS Christian Brauner
  2021-05-28  9:24 ` [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2() Christian Brauner
@ 2021-05-28  9:24 ` Christian Brauner
  2021-05-28 14:20   ` Richard Guy Briggs
  2 siblings, 1 reply; 6+ messages in thread
From: Christian Brauner @ 2021-05-28  9:24 UTC (permalink / raw)
  To: Christoph Hellwig, Al Viro
  Cc: Aleksa Sarai, Richard Guy Briggs, linux-fsdevel, Christian Brauner

From: Christian Brauner <christian.brauner@ubuntu.com>

Test that openat2() rejects unknown flags in the upper 32 bit range.

Cc: Richard Guy Briggs <rgb@redhat.com>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
/* v2 */
- Richard Guy Briggs <rgb@redhat.com>:
  - Rename test to clarify what it actually tests.
---
 tools/testing/selftests/openat2/openat2_test.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c
index 381d874cce99..d7ec1e7da0d0 100644
--- a/tools/testing/selftests/openat2/openat2_test.c
+++ b/tools/testing/selftests/openat2/openat2_test.c
@@ -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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2()
  2021-05-28  9:24 ` [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2() Christian Brauner
@ 2021-05-28 14:19   ` Richard Guy Briggs
  0 siblings, 0 replies; 6+ messages in thread
From: Richard Guy Briggs @ 2021-05-28 14:19 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Christoph Hellwig, Al Viro, Aleksa Sarai, linux-fsdevel,
	Christian Brauner

On 2021-05-28 11:24, Christian Brauner wrote:
> From: Christian Brauner <christian.brauner@ubuntu.com>
> 
> 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().
> 
> There's a snafu here though stripping FMODE_* directly from flags would
> cause the upper 32 bits to be truncated as well due to integer promotion
> rules since FMODE_* is unsigned int, O_* are signed ints (yuck).
> 
> In addition, struct open_flags currently defines flags to be 32 bit
> which is reasonable. If we simply were to bump it to 64 bit we would
> need to change a lot of code preemptively which doesn't seem worth it.
> So simply add a compile-time check verifying that all currently known
> O_* flags are within the 32 bit range and fail to build if they aren't
> anymore.
> 
> This change shouldn't regress old open syscalls since they silently
> truncate any unknown values anyway. It is a tiny semantic change for
> openat2() but it is very unlikely people pass ing > 32 bit unknown flags
> and the syscall is relatively new too.
> 
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org
> Reported-by: Richard Guy Briggs <rgb@redhat.com>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Reviewed-by: Aleksa Sarai <cyphar@cyphar.com>
> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>

Reviewed-by: Richard Guy Briggs <rgb@redhat.com>

> ---
> /* v2 */
> - Richard Guy Briggs <rgb@redhat.com>:
>   - Add an explicit BUILD_BUG_ON() to check when we need to change
>     struct open_flags to account for O_* flags > 32 bits.
> ---
>  fs/open.c | 14 +++++++++++---
>  1 file changed, 11 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/open.c b/fs/open.c
> index e53af13b5835..53bc0573c0ec 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -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
> -- 
> 2.27.0
> 

- RGB

--
Richard Guy Briggs <rgb@redhat.com>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
IRC: rgb, SunRaycer
Voice: +1.647.777.2635, Internal: (81) 32635


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value
  2021-05-28  9:24 ` [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value Christian Brauner
@ 2021-05-28 14:20   ` Richard Guy Briggs
  0 siblings, 0 replies; 6+ messages in thread
From: Richard Guy Briggs @ 2021-05-28 14:20 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Christoph Hellwig, Al Viro, Aleksa Sarai, linux-fsdevel,
	Christian Brauner

On 2021-05-28 11:24, Christian Brauner wrote:
> From: Christian Brauner <christian.brauner@ubuntu.com>
> 
> Test that openat2() rejects unknown flags in the upper 32 bit range.
> 
> Cc: Richard Guy Briggs <rgb@redhat.com>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: linux-fsdevel@vger.kernel.org
> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>

Reviewed-by: Richard Guy Briggs <rgb@redhat.com>

> ---
> /* v2 */
> - Richard Guy Briggs <rgb@redhat.com>:
>   - Rename test to clarify what it actually tests.
> ---
>  tools/testing/selftests/openat2/openat2_test.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c
> index 381d874cce99..d7ec1e7da0d0 100644
> --- a/tools/testing/selftests/openat2/openat2_test.c
> +++ b/tools/testing/selftests/openat2/openat2_test.c
> @@ -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);
> -- 
> 2.27.0
> 

- RGB

--
Richard Guy Briggs <rgb@redhat.com>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
IRC: rgb, SunRaycer
Voice: +1.647.777.2635, Internal: (81) 32635


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-05-28 14:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-28  9:24 [PATCH v2 0/3] openat2: flag fixes Christian Brauner
2021-05-28  9:24 ` [PATCH v2 1/3] fcntl: remove unused VALID_UPGRADE_FLAGS Christian Brauner
2021-05-28  9:24 ` [PATCH v2 2/3] open: don't silently ignore unknown O-flags in openat2() Christian Brauner
2021-05-28 14:19   ` Richard Guy Briggs
2021-05-28  9:24 ` [PATCH v2 3/3] test: add openat2() test for invalid upper 32 bit flag value Christian Brauner
2021-05-28 14:20   ` Richard Guy Briggs

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).