diff mbox series

[v2] Add new open(2) flag - O_EMPTY_PATH

Message ID 20221231235618.117201-1-ahamza@ixsystems.com
State New
Headers show
Series [v2] Add new open(2) flag - O_EMPTY_PATH | expand

Commit Message

Ameer Hamza Dec. 31, 2022, 11:56 p.m. UTC
This patch adds a new flag O_EMPTY_PATH that allows openat and open
system calls to open a file referenced by fd if the path is empty,
and it is very similar to the FreeBSD O_EMPTY_PATH flag. This can be
beneficial in some cases since it would avoid having to grant /proc
access to things like samba containers for reopening files to change
flags in a race-free way.

Signed-off-by: Ameer Hamza <ahamza@ixsystems.com>

---
Change in v2:
add nonconflicting values for O_EMPTY_PATH on architectures
where default conflicts with existing flags.
---
---
 arch/alpha/include/uapi/asm/fcntl.h    | 1 +
 arch/parisc/include/uapi/asm/fcntl.h   | 1 +
 arch/sparc/include/uapi/asm/fcntl.h    | 1 +
 fs/fcntl.c                             | 2 +-
 fs/namei.c                             | 4 ++--
 fs/open.c                              | 2 +-
 include/linux/fcntl.h                  | 2 +-
 include/uapi/asm-generic/fcntl.h       | 4 ++++
 tools/include/uapi/asm-generic/fcntl.h | 4 ++++
 9 files changed, 16 insertions(+), 5 deletions(-)

Comments

kernel test robot Jan. 1, 2023, 11:16 a.m. UTC | #1
Hi Ameer,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on deller-parisc/for-next]
[also build test ERROR on arnd-asm-generic/master linus/master davem-sparc/master v6.2-rc1 next-20221226]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Ameer-Hamza/Add-new-open-2-flag-O_EMPTY_PATH/20230101-075923
base:   https://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux.git for-next
patch link:    https://lore.kernel.org/r/20221231235618.117201-1-ahamza%40ixsystems.com
patch subject: [PATCH v2] Add new open(2) flag - O_EMPTY_PATH
config: sparc64-randconfig-m031-20230101
compiler: sparc64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/8b2a38bbb23d4266a2d8a0c81d97ec4267816fab
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Ameer-Hamza/Add-new-open-2-flag-O_EMPTY_PATH/20230101-075923
        git checkout 8b2a38bbb23d4266a2d8a0c81d97ec4267816fab
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc64 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from <command-line>:
   fs/fcntl.c: In function 'fcntl_init':
>> include/linux/compiler_types.h:358:45: error: call to '__compiletime_assert_317' declared with attribute error: BUILD_BUG_ON failed: 22 - 1 != HWEIGHT32( (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | __FMODE_EXEC | __FMODE_NONOTIFY)
     358 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |                                             ^
   include/linux/compiler_types.h:339:25: note: in definition of macro '__compiletime_assert'
     339 |                         prefix ## suffix();                             \
         |                         ^~~~~~
   include/linux/compiler_types.h:358:9: note: in expansion of macro '_compiletime_assert'
     358 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert'
      39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
         |                                     ^~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:50:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
      50 |         BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
         |         ^~~~~~~~~~~~~~~~
   fs/fcntl.c:1030:9: note: in expansion of macro 'BUILD_BUG_ON'
    1030 |         BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ !=
         |         ^~~~~~~~~~~~


vim +/__compiletime_assert_317 +358 include/linux/compiler_types.h

eb5c2d4b45e3d2 Will Deacon 2020-07-21  344  
eb5c2d4b45e3d2 Will Deacon 2020-07-21  345  #define _compiletime_assert(condition, msg, prefix, suffix) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21  346  	__compiletime_assert(condition, msg, prefix, suffix)
eb5c2d4b45e3d2 Will Deacon 2020-07-21  347  
eb5c2d4b45e3d2 Will Deacon 2020-07-21  348  /**
eb5c2d4b45e3d2 Will Deacon 2020-07-21  349   * compiletime_assert - break build and emit msg if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21  350   * @condition: a compile-time constant condition to check
eb5c2d4b45e3d2 Will Deacon 2020-07-21  351   * @msg:       a message to emit if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21  352   *
eb5c2d4b45e3d2 Will Deacon 2020-07-21  353   * In tradition of POSIX assert, this macro will break the build if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21  354   * supplied condition is *false*, emitting the supplied error message if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21  355   * compiler has support to do so.
eb5c2d4b45e3d2 Will Deacon 2020-07-21  356   */
eb5c2d4b45e3d2 Will Deacon 2020-07-21  357  #define compiletime_assert(condition, msg) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21 @358  	_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
eb5c2d4b45e3d2 Will Deacon 2020-07-21  359
David Laight Jan. 2, 2023, 2:01 p.m. UTC | #2
From: Ameer Hamza
> Sent: 31 December 2022 23:56
> 
> This patch adds a new flag O_EMPTY_PATH that allows openat and open
> system calls to open a file referenced by fd if the path is empty,
> and it is very similar to the FreeBSD O_EMPTY_PATH flag. This can be
> beneficial in some cases since it would avoid having to grant /proc
> access to things like samba containers for reopening files to change
> flags in a race-free way.
> 

But what does it do?
(Apart from add code to a common kernel code path.)

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Ameer Hamza Jan. 2, 2023, 2:35 p.m. UTC | #3
On Mon, Jan 02, 2023 at 02:01:38PM +0000, David Laight wrote:
> From: Ameer Hamza
> > Sent: 31 December 2022 23:56
> > 
> > This patch adds a new flag O_EMPTY_PATH that allows openat and open
> > system calls to open a file referenced by fd if the path is empty,
> > and it is very similar to the FreeBSD O_EMPTY_PATH flag. This can be
> > beneficial in some cases since it would avoid having to grant /proc
> > access to things like samba containers for reopening files to change
> > flags in a race-free way.
> > 
> 
> But what does it do?
> (Apart from add code to a common kernel code path.)
> 
> 	David
It can convert an O_PATH descriptor to one suitable for r/w work.
If we already have a file descriptor: {opath_fd = open(&lt;path&gt;, O_PATH);}, we can call {openat(opath_fd, "", O_EMPTY_PATH | O_RDWR)} instead of going through procfs {open(/proc/self/fd/&lt;opath_fd&gt;, O_RDWR)}.
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)
>
David Laight Jan. 6, 2023, 9:21 a.m. UTC | #4
From: Ameer Hamza
> Sent: 02 January 2023 14:36
> 
> On Mon, Jan 02, 2023 at 02:01:38PM +0000, David Laight wrote:
> > From: Ameer Hamza
> > > Sent: 31 December 2022 23:56
> > >
> > > This patch adds a new flag O_EMPTY_PATH that allows openat and open
> > > system calls to open a file referenced by fd if the path is empty,
> > > and it is very similar to the FreeBSD O_EMPTY_PATH flag. This can be
> > > beneficial in some cases since it would avoid having to grant /proc
> > > access to things like samba containers for reopening files to change
> > > flags in a race-free way.
> > >
> >
> > But what does it do?
> > (Apart from add code to a common kernel code path.)
> >
> > 	David
>
> It can convert an O_PATH descriptor to one suitable for r/w work.
> If we already have a file descriptor: {opath_fd = open(&lt;path&gt;, O_PATH);}, we can call
> {openat(opath_fd, "", O_EMPTY_PATH | O_RDWR)} instead of going through procfs
> {open(/proc/self/fd/&lt;opath_fd&gt;, O_RDWR)}.

Aren't both of those security problems?

Testing the file's inode permission allow write access isn't enough
to verify that the program could actually open the file for writing.
The program also needs 'directory search' access on all the directories
back as far as an open directory fd.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
diff mbox series

Patch

diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h
index 50bdc8e8a271..ea08341bb9fe 100644
--- a/arch/alpha/include/uapi/asm/fcntl.h
+++ b/arch/alpha/include/uapi/asm/fcntl.h
@@ -34,6 +34,7 @@ 
 
 #define O_PATH		040000000
 #define __O_TMPFILE	0100000000
+#define O_EMPTY_PATH	0200000000
 
 #define F_GETLK		7
 #define F_SETLK		8
diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h
index 03dee816cb13..e6144823ee5b 100644
--- a/arch/parisc/include/uapi/asm/fcntl.h
+++ b/arch/parisc/include/uapi/asm/fcntl.h
@@ -19,6 +19,7 @@ 
 
 #define O_PATH		020000000
 #define __O_TMPFILE	040000000
+#define O_EMPTY_PATH	0100000000
 
 #define F_GETLK64	8
 #define F_SETLK64	9
diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h
index 67dae75e5274..08aed1e2b32d 100644
--- a/arch/sparc/include/uapi/asm/fcntl.h
+++ b/arch/sparc/include/uapi/asm/fcntl.h
@@ -37,6 +37,7 @@ 
 
 #define O_PATH		0x1000000
 #define __O_TMPFILE	0x2000000
+#define O_EMPTY_PATH	0x4000000
 
 #define F_GETOWN	5	/*  for sockets. */
 #define F_SETOWN	6	/*  for sockets. */
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 146c9ab0cd4b..7aac650e16e2 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -1027,7 +1027,7 @@  static int __init fcntl_init(void)
 	 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
 	 * is defined as O_NONBLOCK on some platforms and not on others.
 	 */
-	BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
+	BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ !=
 		HWEIGHT32(
 			(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
 			__FMODE_EXEC | __FMODE_NONOTIFY));
diff --git a/fs/namei.c b/fs/namei.c
index 309ae6fc8c99..2b2735af6d03 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -192,7 +192,7 @@  getname_flags(const char __user *filename, int flags, int *empty)
 	if (unlikely(!len)) {
 		if (empty)
 			*empty = 1;
-		if (!(flags & LOOKUP_EMPTY)) {
+		if (!(flags & (LOOKUP_EMPTY | O_EMPTY_PATH))) {
 			putname(result);
 			return ERR_PTR(-ENOENT);
 		}
@@ -2347,7 +2347,7 @@  static const char *path_init(struct nameidata *nd, unsigned flags)
 	if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)
 		return ERR_PTR(-EAGAIN);
 
-	if (!*s)
+	if (!*s && unlikely(!(flags & O_EMPTY_PATH)))
 		flags &= ~LOOKUP_RCU;
 	if (flags & LOOKUP_RCU)
 		rcu_read_lock();
diff --git a/fs/open.c b/fs/open.c
index 82c1a28b3308..b4ec054a418f 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1301,7 +1301,7 @@  static long do_sys_openat2(int dfd, const char __user *filename,
 	if (fd)
 		return fd;
 
-	tmp = getname(filename);
+	tmp = getname_flags(filename, how->flags & O_EMPTY_PATH, NULL);
 	if (IS_ERR(tmp))
 		return PTR_ERR(tmp);
 
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index a332e79b3207..bf8467bb0bd2 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -10,7 +10,7 @@ 
 	(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
 	 O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \
 	 FASYNC	| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
-	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE)
+	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTY_PATH)
 
 /* List of all valid flags for the how->resolve argument: */
 #define VALID_RESOLVE_FLAGS \
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 1ecdb911add8..a03f4275517b 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -89,6 +89,10 @@ 
 #define __O_TMPFILE	020000000
 #endif
 
+#ifndef O_EMPTY_PATH
+#define O_EMPTY_PATH	040000000
+#endif
+
 /* a horrid kludge trying to make sure that this will fail on old kernels */
 #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
 #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT)      
diff --git a/tools/include/uapi/asm-generic/fcntl.h b/tools/include/uapi/asm-generic/fcntl.h
index b02c8e0f4057..f32a81604296 100644
--- a/tools/include/uapi/asm-generic/fcntl.h
+++ b/tools/include/uapi/asm-generic/fcntl.h
@@ -89,6 +89,10 @@ 
 #define __O_TMPFILE	020000000
 #endif
 
+#ifndef O_EMPTY_PATH
+#define O_EMPTY_PATH	040000000
+#endif
+
 /* a horrid kludge trying to make sure that this will fail on old kernels */
 #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
 #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT)