Process & Thread · Section 2
execve(2)
Replace the current process image with a new program.
Signature
#include <unistd.h>
int execve(const char * pathname, char *const [] argv, char *const [] envp);- pathname
- Path to the executable. Absolute or relative; must be a regular file with execute permission or an interpreter script (#!/…) the kernel can load.
- argv
- NULL-terminated array of argument strings. By convention argv[0] is the program name as the caller wishes it to appear.
- envp
- NULL-terminated array of environment strings of the form NAME=value. Pass environ to inherit, or a curated array to scrub sensitive variables.
Description
execve() replaces the calling process's text, data, BSS, and stack segments with those of a new program, while preserving the process ID, parent process ID, open file descriptors (unless O_CLOEXEC is set), pending signals, and resource limits. The new program is given argv as its argument vector and envp as its environment; both arrays must be NULL-terminated. On success, execve() does not return — control transfers to the entry point of the new program. On failure it returns -1 with errno set and the caller continues. Set-user-ID and set-group-ID bits on the new binary may take effect subject to no-new-privs, NSU mounts, and capability rules.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 11 | i386 | sys_execve |
| x64 (x86_64) | 59 | 64 | sys_execve |
| ARM64 (aarch64) | 221 | — | — |
Kernel history
Introduced in Linux 1.0.
1.0
execve() has been part of Linux since 1.0 and largely follows POSIX.1, with Linux-specific behaviour around #! interpreter handling and the ARG_MAX accounting.
3.19
execveat() was added as a directory-fd-relative variant (analogous to openat) and to support executing file descriptors directly (AT_EMPTY_PATH).
5.7
The maximum depth of #! interpreter recursion was hardened against pathological cases that could allow kernel-stack exhaustion via deeply nested interpreter chains.
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
execve() and execveat() are on Docker / Podman default allow-lists. Blocking execve() is the single most effective container hardening for workloads that should never spawn binaries — e.g. a web server that only reads files and writes to sockets. After all required binaries have been started, an init wrapper can install a seccomp filter that denies execve() / execveat(), removing an entire class of post-exploitation moves (shell drops, lateral binary loading). no_new_privs and CAP_SYS_PTRACE removal compose nicely with this.
libseccomp
// Block execve from a process — disables ability to spawn binaries
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(execveat), 0);strace example
$ strace -f -e execve /bin/sh -c 'ls /tmp'
execve("/bin/sh", ["/bin/sh", "-c", "ls /tmp"], 0x7ffd8c4f3a18 /* 28 vars */) = 0
execve("/usr/bin/ls", ["ls", "/tmp"], 0x55a9b2e1d010 /* 28 vars */) = 0
+++ exited with 0 +++strace -f follows fork/clone children, so execve from sub-shells is captured. The third argument (envp) is shown as 0x… /* N vars */ by default — pass --strings-in-hex=none -v to expand. Use -e trace=execve,execveat to filter.
Security & observability
execve() is the canonical post-exploitation primitive — every shell drop, every binary loader, every privilege-escalation path passes through it. The audit subsystem fires execve records (-a always,exit -F arch=b64 -S execve), and the eBPF tracepoint sched_process_exec captures the program name, parent, and environment. Rootkits sometimes hook execve to substitute /bin/ps with a filtered version, or to spawn a hidden binary on certain inputs; comparing tracepoint output against /proc/<pid>/exe is the standard cross-check. For container security, an unexpected execve from a workload pod is almost always worth paging on.
Errors
- E2BIG
- The combined argv + envp + auxiliary vector exceeds the kernel's ARG_MAX limit (typically 128 KiB on Linux, or 1/4 of RLIMIT_STACK on newer kernels).
- EACCES
- Search permission denied on a path component, or pathname is not executable, or the filesystem is mounted noexec.
- EFAULT
- —
- EINVAL
- —
- EIO
- —
- EISDIR
- —
- ELIBBAD
- An ELF interpreter (e.g. ld.so) named in the binary could not be loaded.
- ELOOP
- Too many symbolic-link or interpreter levels were traversed.
- EMFILE
- —
- ENAMETOOLONG
- —
- ENFILE
- —
- ENOENT
- —
- ENOEXEC
- The file is recognised but is not in an executable format the kernel can run (e.g. wrong architecture, broken ELF).
- ENOMEM
- —
- ENOTDIR
- —
- EPERM
- The filesystem is mounted nosuid and the binary is set-UID/GID, or no_new_privs blocks privilege elevation.
- ETXTBSY
- The binary is currently open for writing by another process; execution would race with that writer.