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.
#include <signal.h>
#include <sys/wait.h>'''],
['mallinfo', '''#include <malloc.h>'''],
+ ['execveat', '''#include <unistd.h>'''],
['close_range', '''#include <unistd.h>'''],
]
# 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)
#include "fileio.h"
#include "hashmap.h"
#include "macro.h"
+#include "missing_syscall.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "serialize.h"
}
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