$RodHat_
Syscall reference · Processes

execveat(2)

Execute a program via file descriptor instead of path, eliminating the TOCTOU race between verify and exec.

Since
Linux 3.19check your kernel →
glibc
2.34
System
Linux
Man page
execveat(2)

Synopsis

#include <fcntl.h>
#include <unistd.h>

int execveat(int dirfd, const char *pathname,
             const char *const argv[], const char *const envp[],
             int flags);

Rod's take

execveat(2) replaces execve(2)'s path argument with a file descriptor, closing the TOCTOU window between verification and execution. With AT_EMPTY_PATH and an O_PATH fd, you exec exactly the inode you opened, even if the file is later unlinked or replaced. Works with memfds for in-memory ELF execution. glibc wrapper arrived in 2.34; use syscall(2) on older systems.

Flags and arguments that matter

NameWhat it does
AT_EMPTY_PATHWhen pathname is "", treat dirfd as the file to execute directly. dirfd must reference a regular file (O_PATH or O_RDONLY). Enables exec of an already-open, possibly-unlinked inode.
AT_SYMLINK_NOFOLLOWFail with ELOOP if the last component of pathname is a symlink. Not applicable with AT_EMPTY_PATH.

Errors you will actually hit

errnoMeaning
ELOOPAT_SYMLINK_NOFOLLOW set and last pathname component is a symlink.
ENOENTAT_EMPTY_PATH not set and pathname resolves to nothing.
EACCESExecute permission denied on the file; x bit checked even for O_PATH fds.

Example

#include <fcntl.h>
#include <unistd.h>

int fd = open("/usr/local/bin/worker", O_PATH);
/* verify the binary here */
char *argv[] = { "worker", NULL };
char *envp[] = { NULL };
execveat(fd, "", argv, envp, AT_EMPTY_PATH);
/* not reached on success */

Trimmed from the full walkthrough in execveat(2): execute a binary you already have open.

BSD equivalent

FreeBSD has fexecve(3), which achieves the same result via /dev/fd/N rather than a native syscall. OpenBSD does not expose fexecve.

The long version, from the console

Sources

  1. execveat(2), man7.org
  2. execveat: execute program relative to a file descriptor, LWN.net

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.