[PATCH] Linux: Add execveat system call wrapper
Adhemerval Zanella
adhemerval.zanella@linaro.org
Thu Apr 30 12:28:26 GMT 2020
On 30/04/2020 08:15, Florian Weimer via Libc-alpha wrote:
> So it turns out that openat and execveat have totally different flags
> arguments, and translation is needed.
>
> The execveat manual page says this:
>
> AT_EMPTY_PATH
> If pathname is an empty string, operate on the file referred to
> by dirfd (which may have been obtained using the open(2) O_PATH
> flag).
>
> AT_SYMLINK_NOFOLLOW
> If the file identified by dirfd and a non-NULL pathname is a
> symbolic link, then the call fails with the error ELOOP.
>
> fs/exec.c in the kernel sources handles flags in the do_open_execat
> function:
>
> if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> return ERR_PTR(-EINVAL);
> if (flags & AT_SYMLINK_NOFOLLOW)
> open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> if (flags & AT_EMPTY_PATH)
> open_exec_flags.lookup_flags |= LOOKUP_EMPTY;
>
> file = do_filp_open(fd, name, &open_exec_flags);
>
> So the manual page is correct and there are only two flags to support.
>
> So I think we have to do this:
>
> * If there are more flags than just the two, fail with EINVAL.
>
> * To handle AT_EMPTY_PATH, do not open a new file descriptor (using
> openat) if AT_EMPTY_PATH is specified *and* the file name is "".
>
> * To handle AT_SYMLINK_NOFOLLOW, openat needs to be called with
> O_NOFOLLOW in that case (in addition to O_CLOEXEC).
These will surely need to be on the testcase.
>
> The behavior with AT_EMPTY_PATH/"" and AT_SYMLINK_NOFOLLOW at the same
> time is not immedately obvious from the kernel code, so I wrote a small
> test program (/bin/sh is a symbolic link to /bin/bash on this system):
>
> #include <err.h>
> #include <fcntl.h>
> #include <sys/syscall.h>
> #include <unistd.h>
>
> int
> main (void)
> {
> int fd = open ("/bin/sh", O_PATH | O_NOFOLLOW);
> if (fd < 0)
> err (1, "open");
> static char *const argv[] = { "sh", "-c", "exit 0", NULL };
> static char *const envp[] = { NULL };
> syscall (SYS_execveat, fd, "", argv, envp,
> AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
> err (1, "execveat");
> }
>
> This fails:
>
> openat(AT_FDCWD, "/bin/sh", O_RDONLY|O_NOFOLLOW|O_PATH) = 3
> execveat(3, "", ["sh", "-c", "exit 0"], 0x402040 /* 0 vars */, AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) = -1 ELOOP (Too many levels of symbolic links)
> […]
> execveat-opath-symlink: execveat: Too many levels of symbolic links
>
> So I think for the AT_EMPTY_PATH/"" and AT_SYMLINK_NOFOLLOW case, we
> need to call fstatat64 with AT_EMPTY_PATH and see if st_mode indicates
> that the descriptor refers to a symbolic link. If it does, the function
> needs to fail with ELOOP.
I think execve would handle it:
openat(AT_FDCWD, "/bin/sh", O_RDONLY|O_NOFOLLOW|O_PATH) = 3
execve("/proc/self/fd/3", ["sh", "-c", "echo test"], 0x556815e580a8 /* 0 vars */) = -1 ELOOP (Too many levels of symbolic links)
>
> AT_EMPTY_PATH without a "" file name does not need special treatment and
> can use the regular openat path (with the conditional setting of
> O_NOFOLLOW).
More information about the Libc-alpha
mailing list