shared/exec-util: use our own execveat() wrapper instead of fexecve()

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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-09-23 16:23:30 +02:00
parent a6d9111c67
commit 8939eeae52
3 changed files with 24 additions and 3 deletions

View 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>'''],
]

View 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)

View 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