Linux: Add execveat system call wrapper

Message ID 20210315214205.2107019-1-ahajkova@redhat.com
State Superseded
Headers
Series Linux: Add execveat system call wrapper |

Commit Message

Alexandra Hájková March 15, 2021, 9:42 p.m. UTC
  From: Alexandra Hájková <ahajkova@redhat.com>

 Also add the test for the new wrapper.
---
This patch depends on Florian Weimer's patch: "Add support for static
internal tests" which haven't been reviewed yet
This versiond adds:
* check the fallback mechanism with
an internal test
* uses xfchmod, xchdir and xstat
* some cosmetics

 NEWS                                          |   5 +
 include/unistd.h                              |   4 +
 posix/Makefile                                |   4 +-
 posix/Versions                                |   3 +
 posix/execveat.c                              |  41 ++++
 posix/tst-execveat-compat.c                   |  32 ++++
 posix/tst-execveat.c                          | 180 ++++++++++++++++++
 posix/unistd.h                                |   5 +
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/execveat.c            |  54 ++++++
 sysdeps/unix/sysv/linux/execveat_fallback.c   |  69 +++++++
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   2 +-
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 41 files changed, 427 insertions(+), 2 deletions(-)
 create mode 100644 posix/execveat.c
 create mode 100644 posix/tst-execveat-compat.c
 create mode 100644 posix/tst-execveat.c
 create mode 100644 sysdeps/unix/sysv/linux/execveat.c
 create mode 100644 sysdeps/unix/sysv/linux/execveat_fallback.c
  

Comments

Joseph Myers March 15, 2021, 10:12 p.m. UTC | #1
Since 2.33 has been released, this needs updating to use 2.34 symbol 
versions and to put the NEWS entry in the NEWS section for 2.34 not that 
for 2.33.
  
Dmitry V. Levin March 15, 2021, 10:17 p.m. UTC | #2
On Mon, Mar 15, 2021 at 10:42:05PM +0100, Alexandra Hájková via Libc-alpha wrote:
[...]
> +/* Replace the current process, executing PATH relative to DIFRD with
> + * arguments ARGV and environment ENVP.
> + * ARGV and ENVP are terminated by NULL pointers.  */
> +int
> +__execveat (int dirfd, const char *path, char *const argv[], char *const envp[],
> +            int flags)
> +{
> +  if (difrd < 0 || path == NULL || argv == NULL || envp == NULL)
> +    {
> +      __set_errno (EINVAL);
> +      return -1;
> +    }
> +
> +  __set_errno (ENOSYS);
> +  return -1;
> +}
> +stub_warning (execveat)

I'm not sure how picky we are about stubs, but "difrd < 0" looks odd
anyway: it rules out AT_FDCWD, and if "path" is absolute, then "dirfd"
shall be ignored anyway.  I suggest to drop this "difrd < 0" check.
  

Patch

diff --git a/NEWS b/NEWS
index aa0f10a891..4fa20e2fa3 100644
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,11 @@  Major new features:
   The latest GCC available at this time (10.2) does not support this level of
   fortification.
 
+* The function execveat has been added and it operates similar to execve.
+  The syscall is already used to implement fexecve without requiring /proc to
+  be mounted.  Similar to fexecve, if the syscall is not supported a fallback
+  which access /proc is used.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The mallinfo function is marked deprecated.  Callers should call
diff --git a/include/unistd.h b/include/unistd.h
index 54becbc9eb..7296164af8 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -192,6 +192,10 @@  extern int __libc_pause (void);
 extern int __getlogin_r_loginuid (char *name, size_t namesize)
      attribute_hidden;
 
+extern int
+__execveat_fallback (int dirfd, const char *path, char *const argv[],
+                     char *const envp[], int flags);
+
 #  if IS_IN (rtld)
 #   include <dl-unistd.h>
 #  endif
diff --git a/posix/Makefile b/posix/Makefile
index be0c72f0bb..f0ebe34c8e 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -66,7 +66,8 @@  routines :=								      \
 	posix_madvise							      \
 	get_child_max sched_cpucount sched_cpualloc sched_cpufree \
 	streams-compat \
-	shm-directory
+	shm-directory                                                         \
+	execveat
 
 aux		:= init-posix environ
 tests		:= test-errno tstgetopt testfnm runtests runptests \
@@ -118,6 +119,7 @@  endif
 
 tests-internal	:= bug-regex5 bug-regex20 bug-regex33 \
 		   tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3
+tests-static-internal += tst-execveat-compat
 tests-container := bug-ga2
 xtests		:= tst-getaddrinfo4 tst-getaddrinfo5 tst-sched_rr_get_interval
 ifeq (yes,$(build-shared))
diff --git a/posix/Versions b/posix/Versions
index cfd3819966..486c03d8f2 100644
--- a/posix/Versions
+++ b/posix/Versions
@@ -147,6 +147,9 @@  libc {
   }
   GLIBC_2.30 {
   }
+  GLIBC_2.33 {
+    execveat;
+  }
   GLIBC_PRIVATE {
     __libc_fork; __libc_pread; __libc_pwrite;
     __nanosleep_nocancel; __pause_nocancel;
diff --git a/posix/execveat.c b/posix/execveat.c
new file mode 100644
index 0000000000..b79492bf93
--- /dev/null
+++ b/posix/execveat.c
@@ -0,0 +1,41 @@ 
+/* Execute program relative to a directory file descriptor.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <unistd.h>
+
+/* Replace the current process, executing PATH relative to DIFRD with
+ * arguments ARGV and environment ENVP.
+ * ARGV and ENVP are terminated by NULL pointers.  */
+int
+__execveat (int dirfd, const char *path, char *const argv[], char *const envp[],
+            int flags)
+{
+  if (difrd < 0 || path == NULL || argv == NULL || envp == NULL)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  __set_errno (ENOSYS);
+  return -1;
+}
+stub_warning (execveat)
+
+weak_alias (__execveat, execveat)
diff --git a/posix/tst-execveat-compat.c b/posix/tst-execveat-compat.c
new file mode 100644
index 0000000000..082c67690d
--- /dev/null
+++ b/posix/tst-execveat-compat.c
@@ -0,0 +1,32 @@ 
+/* Test the fallback implementation of execveat.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Get the declaration of the official execveat function.  */
+#include <unistd.h>
+
+/* Compile a local version of execveat.  */
+#include <sysdeps/unix/sysv/linux/execveat_fallback.c>
+
+int
+__execveat_fallback (int dirfd, const char *path, char *const argv[],
+                     char *const envp[], int flags);
+
+/* Re-use the test, but run it against copy_file_range_compat defined
+   above.  */
+#define execveat execveat_fallback
+#include "tst-execveat.c"
diff --git a/posix/tst-execveat.c b/posix/tst-execveat.c
new file mode 100644
index 0000000000..52779de8cd
--- /dev/null
+++ b/posix/tst-execveat.c
@@ -0,0 +1,180 @@ 
+/* Test execveat at the various corner cases.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+#include <wait.h>
+#include <support/test-driver.h>
+
+int
+call_execveat (int fd, const char *pathname, int flags, int expected_fail,
+               int num)
+{
+  char *envp[] = { (char *) "FOO=3", NULL };
+  char *argv[] = { (char *) "sh", (char *) "-c", (char *) "exit $FOO", NULL };
+  pid_t pid;
+  int status;
+
+  if (test_verbose > 0)
+    printf ("call line number: %d\n", num);
+
+  pid = xfork ();
+  if (pid == 0)
+    {
+      TEST_COMPARE (execveat (fd, pathname, argv, envp, flags), -1);
+      if (errno == ENOSYS)
+        FAIL_UNSUPPORTED ("execveat is unimplemented");
+      else if (errno == expected_fail)
+        {
+          if (test_verbose > 0)
+            printf ("expected fail: errno %d\n", errno);
+          _exit (0);
+        }
+      else
+        FAIL_EXIT1 ("execveat failed: %m (%d)", errno);
+    }
+  xwaitpid (pid, &status, 0);
+
+  if (WIFEXITED (status))
+    {
+      if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
+        FAIL_UNSUPPORTED ("execveat is unimplemented");
+      else if (expected_fail != 0)
+        TEST_COMPARE (WEXITSTATUS (status), 0);
+      else
+        TEST_COMPARE (WEXITSTATUS (status), 3);
+    }
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  DIR *dirp;
+  int fd, fd_out;
+  char *tmp_dir, *symlink_name, *tmp_sh;
+  struct stat64 st;
+
+  dirp = opendir ("/bin");
+  if (dirp == NULL)
+    FAIL_EXIT1 ("failed to open /bin");
+  fd = dirfd (dirp);
+
+  /* Call execveat for various fd/pathname combinations.  */
+
+  /* Check the pathname relative to a valid dirfd.  */
+  call_execveat (fd, "sh", 0, 0, __LINE__);
+  xchdir ("/bin");
+  /* Use the special value AT_FDCWD as dirfd. Quoting open(2):
+     If pathname is relative and dirfd is the special value AT_FDCWD, then
+     pathname is interpreted relative to the current working directory of
+     the calling process.  */
+  call_execveat (AT_FDCWD, "sh", 0, 0, __LINE__);
+  xclose (fd);
+#ifdef O_PATH
+  /* Check the pathname relative to a valid dirfd with O_PATH.  */
+  fd = xopen ("/bin", O_PATH | O_DIRECTORY, O_RDONLY);
+  call_execveat (fd, "sh", 0, 0, __LINE__);
+  xclose (fd);
+
+  /* Check absolute pathname, dirfd should be ignored.  */
+  call_execveat (AT_FDCWD, "/bin/sh", 0, 0, __LINE__);
+  fd = xopen ("/usr", O_PATH | O_DIRECTORY, 0);
+  /* Same check for absolute pathname, but with input file descriptor
+     openend with different flags.  The dirfd should be ignored.  */
+  call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
+  xclose (fd);
+#endif
+
+  fd = xopen ("/usr", O_RDONLY, 0);
+  /* Same check for absolute pathname, but with input file descriptor
+     openend with different flags.  The dirfd should be ignored.  */
+  call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
+  xclose (fd);
+
+  fd = xopen ("/bin/sh", O_RDONLY, 0);
+  /* Check relative pathname, where dirfd does not point to a directory.  */
+  call_execveat (fd, "sh", 0, ENOTDIR, __LINE__);
+  /* Check absolute pathname, but dirfd is a regular file.  The dirfd
+     should be ignored.  */
+  call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
+  xclose (fd);
+
+#ifdef O_PATH
+  /* Quoting open(2): O_PATH
+     Obtain a file descriptor that can be used for two purposes: to
+     indicate a location in the filesystem tree and to perform
+     operations that act purely at the file descriptor level.  */
+  fd = xopen ("/bin/sh", O_PATH, 0);
+  /* Check the empty pathname.  Dirfd is a regular file with O_PATH.  */
+  call_execveat (fd, "", 0, ENOENT, __LINE__);
+  /* Same check for an empty pathname, but with AT_EMPTY_PATH flag.
+     Quoting open(2):
+     If oldpath is an empty string, create a link to the file referenced
+     by olddirfd (which may have been obtained using the open(2) O_PATH flag. */
+  call_execveat (fd, "", AT_EMPTY_PATH, 0, __LINE__);
+  call_execveat (fd, "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 0, __LINE__);
+  xclose (fd);
+
+  /* Create a temporary directory "tmp_dir" and create a symbolik link tmp_sh
+     pointing to /bin/sh inside the tmp_dir. Open dirfd as a symbolic link.  */
+  tmp_dir = support_create_temp_directory ("tst-execveat_dir");
+  symlink_name = xasprintf ("%s/symlink", tmp_dir);
+  xsymlink ("tmp_sh", symlink_name);
+  add_temp_file (symlink_name);
+  tmp_sh = xasprintf ("%s/tmp_sh", tmp_dir);
+  add_temp_file (tmp_sh);
+  fd_out = xopen (symlink_name, O_CREAT | O_WRONLY, 0);
+  xstat ("/bin/sh", &st);
+  fd = xopen ("/bin/sh", O_RDONLY, 0);
+  xcopy_file_range (fd, 0, fd_out, 0, st.st_size, 0);
+  xfchmod (fd_out, 0700);
+  xclose (fd);
+  xclose (fd_out);
+  fd_out = xopen (symlink_name, O_PATH, 0);
+
+ /* Check the empty pathname. Dirfd is a symbolic link.  */
+  call_execveat (fd_out, "", 0, ENOENT, __LINE__);
+  call_execveat (fd_out, "", AT_EMPTY_PATH, 0, __LINE__);
+  call_execveat (fd_out, "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 0,
+                 __LINE__);
+  xclose (fd_out);
+  free (symlink_name);
+  free (tmp_sh);
+  free (tmp_dir);
+#endif
+
+  /* Call execveat with closed fd, we expect this to fail with EBADF.  */
+  call_execveat (fd, "sh", 0, EBADF, __LINE__);
+  /* Call execveat with closed fd, we expect this to pass because the pathname is
+     absolute.  */
+  call_execveat (fd, "/bin/sh", 0, 0, __LINE__);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/posix/unistd.h b/posix/unistd.h
index 3f22763379..d9d8929f71 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -295,6 +295,11 @@  extern int euidaccess (const char *__name, int __type)
 /* An alias for `euidaccess', used by some other systems.  */
 extern int eaccess (const char *__name, int __type)
      __THROW __nonnull ((1));
+
+/* Execute program relative to a directory file descriptor.  */
+extern int execveat (int __fd, const char *__path, char *const __argv[],
+                     char *const __envp[], int __flags)
+    __THROW __nonnull ((2, 3));
 #endif
 
 #ifdef __USE_ATFILE
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index e10a286d2e..6571c0483a 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2192,6 +2192,7 @@  GLIBC_2.32 thrd_equal F
 GLIBC_2.32 thrd_sleep F
 GLIBC_2.32 thrd_yield F
 GLIBC_2.33 __x86_get_cpuid_feature_leaf F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index bac795262d..30b0a2b6f5 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2160,6 +2160,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 897f70db22..af137727ae 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2242,6 +2242,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 094236f713..99dbdc8108 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -144,6 +144,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 2bb4d31e81..c63f471d05 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -141,6 +141,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index d4291fecfb..a2757b60c2 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2104,6 +2104,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/execveat.c b/sysdeps/unix/sysv/linux/execveat.c
new file mode 100644
index 0000000000..0576f36b98
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/execveat.c
@@ -0,0 +1,54 @@ 
+/* Execute program relative to a directory file descriptor.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <sysdep.h>
+#include <sys/syscall.h>
+#include <kernel-features.h>
+#include <fd_to_filename.h>
+#include <not-cancel.h>
+#ifndef __ASSUME_EXECVEAT
+# include "execveat_fallback.c"
+#endif
+
+/* Execute the file FD refers to, overlaying the running program image.
+   ARGV and ENVP are passed to the new program, as for `execve'.  */
+int
+execveat (int dirfd, const char *path, char *const argv[], char *const envp[],
+          int flags)
+{
+  /* Avoid implicit array coercion in syscall macros.  */
+    INLINE_SYSCALL_CALL (execveat, dirfd, path, &argv[0], &envp[0], flags);
+#ifndef __ASSUME_EXECVEAT
+  if (errno != ENOSYS)
+    return -1;
+
+  if ((flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) != 0)
+      return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
+
+  return __execveat_fallback (dirfd, path, argv, envp, flags);
+#endif
+
+  return -1;
+}
diff --git a/sysdeps/unix/sysv/linux/execveat_fallback.c b/sysdeps/unix/sysv/linux/execveat_fallback.c
new file mode 100644
index 0000000000..c4a37d3799
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/execveat_fallback.c
@@ -0,0 +1,69 @@ 
+/* Execute program relative to a directory file descriptor.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <sysdep.h>
+#include <sys/syscall.h>
+#include <kernel-features.h>
+#include <fd_to_filename.h>
+#include <not-cancel.h>
+
+int
+__execveat_fallback (int dirfd, const char *path, char *const argv[],
+                   char *const envp[], int flags)
+{
+    int fd;
+
+    if (path[0] == '\0' && (flags & AT_EMPTY_PATH) && dirfd >= 0)
+        fd = dirfd;
+    else
+    {
+        int oflags = O_CLOEXEC;
+        if (flags & AT_SYMLINK_NOFOLLOW)
+            oflags |= O_NOFOLLOW;
+        fd = __openat_nocancel (dirfd, path, oflags);
+    }
+    if (fd < 0)
+        return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADFD);
+
+    struct fd_to_filename fdfilename;
+    const char *gfilename = __fd_to_filename (fd, &fdfilename);
+
+    /* We do not need the return value.  */
+    __execve (gfilename, argv, envp);
+
+    int save = errno;
+
+    /* We come here only if the 'execve' call fails.  Determine whether
+       /proc is mounted.  If not we return ENOSYS.  */
+    struct stat64 st;
+    if (__stat64 (FD_TO_FILENAME_PREFIX, &st) != 0 && errno == ENOENT)
+        save = ENOSYS;
+
+    if (fd != dirfd)
+        __close_nocancel_nostatus (fd);
+    __set_errno (save);
+
+    return -1;
+}
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 1fd2a862f6..adb1ec3045 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2063,6 +2063,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 943331f01e..c49865ffb6 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2230,6 +2230,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 __x86_get_cpuid_feature_leaf F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index f530151bde..ca2c0325a5 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2095,6 +2095,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 6e76b6dcaa..f7e2a1c1c6 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -145,6 +145,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 7541b8289f..41c37f5ec2 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2175,6 +2175,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 6cf1936c42..e39ccd2e30 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2155,6 +2155,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 98730ebcda..7d8c254d35 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2152,6 +2152,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 92fa6cbc73..7232f19c34 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2146,6 +2146,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 265a49e74e..b6877dec6a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2144,6 +2144,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index cfa5e1111b..1000248984 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2152,6 +2152,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 8c03ac52cd..289d9d9131 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2146,6 +2146,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 17f5609e06..7023145dfe 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2193,6 +2193,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 76a16e2a6d..66ad1cf003 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2202,6 +2202,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 697f072fd4..134265e333 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2235,6 +2235,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 2647bb51f1..ba1dda9adc 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2065,6 +2065,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 036b1c8345..b814ea3217 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2355,6 +2355,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index fb5ad9909f..c191ed5573 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2122,6 +2122,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index cead75acc5..e0ea7697c8 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2200,6 +2200,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 31366dd7e6..e81390ffe9 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2101,6 +2101,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index a3a8be8910..2d0a38c7c7 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2070,6 +2070,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 8f505c5045..7bbeb33ae7 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2067,6 +2067,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 53ef6304f1..3ce6be125a 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2191,6 +2191,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index eba0cb156d..2ab9d0e7fd 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2118,6 +2118,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 17ce5dfd58..d555d074e3 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2076,7 +2076,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
-GLIBC_2.33 __x86_get_cpuid_feature_leaf F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 17a1c83903..b12a0a4520 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2174,6 +2174,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 __x86_get_cpuid_feature_leaf F
+GLIBC_2.33 execveat F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F