]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared/exec-util: use our own execveat() wrapper instead of fexecve()
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 23 Sep 2020 14:23:30 +0000 (16:23 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 6 Nov 2020 14:20:34 +0000 (15:20 +0100)
For scripts, when we call fexecve(), on new kernels glibc calls execveat(),
which fails with ENOENT, and then we fall back to execve() which succeeds:
[pid 63039] execveat(3, "", ["/home/zbyszek/src/systemd/test/test-path-util/script.sh", "--version"], 0x7ffefa3633f0 /* 0 vars */, AT_EMPTY_PATH) = -1 ENOENT (No such file or directory)
[pid 63039] execve("/home/zbyszek/src/systemd/test/test-path-util/script.sh", ["/home/zbyszek/src/systemd/test/test-path-util/script.sh", "--version"], 0x7ffefa3633f0 /* 0 vars */) = 0

But on older kernels glibc (some versions?) implement a fallback which falls
into the same trap with bash $0:
[pid 13534] execve("/proc/self/fd/3", ["/home/test/systemd/test/test-path-util/script.sh", "--version"], 0x7fff84995870 /* 0 vars */) = 0

We don't want that, so let's call execveat() ourselves. Then we can do the
execve() fallback as we want.

meson.build
src/basic/missing_syscall.h
src/shared/exec-util.c

index 04cb63d9217c68b4c0615a04f64d97ba910fbdc0..68ead1f849fc59380345d80bf8d8fb7fcfee2165 100644 (file)
@@ -533,6 +533,7 @@ foreach ident : [
                                  #include <signal.h>
                                  #include <sys/wait.h>'''],
         ['mallinfo',          '''#include <malloc.h>'''],
+        ['execveat',          '''#include <unistd.h>'''],
         ['close_range',       '''#include <unistd.h>'''],
 ]
 
index 01fec6f2f5d1f7190c1512c3d0da87835efea0d5..43d08bada72df68444f383c2b3394a72d7f3e349 100644 (file)
@@ -735,6 +735,25 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info)
 #  define rt_sigqueueinfo missing_rt_sigqueueinfo
 #endif
 
+/* ======================================================================= */
+#if !HAVE_EXECVEAT
+static inline int missing_execveat(int dirfd, const char *pathname,
+                                   char *const argv[], char *const envp[],
+                                   int flags) {
+#  if defined __NR_execveat && __NR_execveat >= 0
+        return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
+#  else
+        errno = ENOSYS;
+        return -1;
+#  endif
+}
+#  undef AT_EMPTY_PATH
+#  define AT_EMPTY_PATH 0x1000
+#  define execveat missing_execveat
+#endif
+
 /* ======================================================================= */
 
 #define systemd_NR_close_range systemd_SC_arch_bias(436)
index a0bb1567ced1aafc15f8bb65f786f986732e97a6..d4ebeea301b4fcd948c21df55ff03da8b8711b7c 100644 (file)
@@ -16,6 +16,7 @@
 #include "fileio.h"
 #include "hashmap.h"
 #include "macro.h"
+#include "missing_syscall.h"
 #include "process-util.h"
 #include "rlimit-util.h"
 #include "serialize.h"
@@ -445,9 +446,9 @@ ExecCommandFlags exec_command_flags_from_string(const char *s) {
 }
 
 int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) {
-        fexecve(executable_fd, argv, envp);
-        if (errno == ENOENT)
-                /* A script? Let's fall back to execve().
+        execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH);
+        if (IN_SET(errno, ENOSYS, ENOENT))
+                /* Old kernel or a script? Let's fall back to execve().
                  *
                  * fexecve(3): "If fd refers to a script (i.e., it is an executable text file that names a
                  * script interpreter with a first line that begins with the characters #!) and the