[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