logo

execve vs fexecve vs execveat

The primary difference between execve(), fexecve(), and execveat() lies in how you specify the target binary file to the Linux kernel. They all perform the exact same core function—wiping the current process memory space and executing a new program—but they accept different inputs to locate the file.

execve() — The Classic Path-Based Execution

  • Input Signature: int execve(const char *pathname, char *const argv[], char *const envp[]);
  • How it works: You must provide a standard string path to the binary (e.g., "/usr/bin/ls").
  • The Catch: The kernel must resolve the path string manually. If a malicious user alters the directories or replaces the file after your program checks it but before the kernel runs it (a Time-of-Check to Time-of-Use or TOCTOU bug), you could execute the wrong file.

fexecve() — The Secure File-Descriptor Execution

  • Input Signature: int fexecve(int fd, char *const argv[], char *const envp[]);
  • How it works: Instead of a string path, you pass an already-open File Descriptor (fd) pointing to the binary.
  • Why use it: This is incredibly secure. Once a process opens a file descriptor to a specific file on the disk, that descriptor locks onto that exact file. Even if an attacker renames, moves, or deletes the original file path on the disk, the file descriptor remains completely unaffected. It is highly favored in secure scripting runtimes, sandboxes, and cryptographic verification loops.

execveat() — The Flexible Hybrid (Relative Paths)

  • Input Signature: int execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags);
  • How it works: It combines paths and file descriptors. It evaluates the string pathname relative to the directory represented by the open file descriptor dirfd.
  • Special Superpowers (Flags):
    • If you pass an absolute path (like "/bin/ls"), it ignores dirfd completely and acts exactly like execve().
    • If you set pathname to an empty string "" and pass the flag AT_EMPTY_PATH, it executes directly from the dirfd file descriptor, acting exactly like fexecve(). Under the hood in Linux, fexecve() is actually implemented as a wrapper library function that calls execveat().

Summary Feature Comparison

Function Core Target Input Main Use Case Linux Kernel Introduction
execve() String Path ("/bin/ls") Everyday standard process execution. Since the beginning (POSIX)
fexecve() File Descriptor (fd) Secure execution; prevents TOCTOU exploits. Added in glibc 2.11
execveat() Directory Descriptor + Relative Path Executing binaries within custom sandboxes or containers. Linux Kernel 3.19

Are you looking to implement one of these to prevent a race condition security bug in your code, or are you building a sandbox/container system that restricts absolute paths?