Skip to content
/linux-syscalls

Processus & Threads · Section 2

execve(2)

Remplace l'image du processus courant par un nouveau programme.

Signature

#include <unistd.h>

int execve(const char * pathname, char *const [] argv, char *const [] envp);
pathname
Chemin de l'exécutable. Absolu ou relatif ; doit être un fichier ordinaire avec permission d'exécution ou un script interprété (#!/…) chargeable par le noyau.
argv
Tableau d'arguments terminé par NULL. Par convention argv[0] est le nom du programme tel que l'appelant veut le voir apparaître.
envp
Tableau d'environnement terminé par NULL, sous forme NOM=valeur. Passer environ pour hériter, ou un tableau filtré pour nettoyer les variables sensibles.

Description

execve() remplace les segments texte, données, BSS et pile du processus appelant par ceux d'un nouveau programme, tout en conservant le PID, le PID parent, les descripteurs de fichier ouverts (sauf si O_CLOEXEC est positionné), les signaux en attente et les limites de ressources. Le nouveau programme reçoit argv comme vecteur d'arguments et envp comme environnement ; les deux tableaux doivent être terminés par NULL. En cas de succès, execve() ne retourne pas — le contrôle passe au point d'entrée du nouveau programme. En cas d'échec elle retourne -1 avec errno positionné et l'appelant poursuit. Les bits set-UID / set-GID peuvent s'appliquer sous réserve de no_new_privs, des montages nosuid et des règles de capacités.

Numéros par architecture

ArchitectureNuméroABIPoint d'entrée
x86 (i386)11i386sys_execve
x64 (x86_64)5964sys_execve
ARM64 (aarch64)221——

Historique noyau

Introduit dans Linux 1.0.

  1. 1.0

    execve() est présent dans Linux depuis 1.0 et suit POSIX.1 dans l'ensemble, avec des comportements spécifiques Linux autour des interpréteurs #! et de la comptabilité ARG_MAX.

  2. 3.19

    execveat() a été ajouté comme variante relative à un descripteur de répertoire (analogue à openat) et pour permettre d'exécuter un descripteur de fichier directement (AT_EMPTY_PATH).

  3. 5.7

    La profondeur maximale de récursion d'interpréteurs #! a été durcie contre les cas pathologiques pouvant provoquer un épuisement de la pile noyau via des chaînes profondément imbriquées.

seccomp & conteneurs

Docker default profile

Autorisé

Podman default profile

Autorisé

execve() et execveat() figurent dans les profils par défaut de Docker / Podman. Le blocage de execve() est le durcissement de conteneur le plus efficace pour les charges qui ne doivent jamais lancer de binaire — par ex. un serveur web qui ne fait que lire des fichiers et écrire sur des sockets. Après le lancement des binaires nécessaires, un wrapper init peut installer un filtre seccomp refusant execve() / execveat(), éliminant toute une classe de mouvements post-exploitation (drop de shell, chargement latéral). no_new_privs et le retrait de CAP_SYS_PTRACE se combinent bien.

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);

Exemple strace

$ 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 suit les enfants fork/clone, donc execve depuis un sous-shell est capturé. Le troisième argument (envp) est affiché 0x… /* N vars */ par défaut — passer --strings-in-hex=none -v pour le déployer. Utiliser -e trace=execve,execveat pour filtrer.

Sécurité & observabilité

execve() est la primitive post-exploitation canonique — tout drop de shell, tout chargeur de binaire, tout chemin d'élévation de privilèges y passe. Le sous-système audit émet des enregistrements execve (-a always,exit -F arch=b64 -S execve), et le tracepoint eBPF sched_process_exec capture le nom du programme, le parent et l'environnement. Certains rootkits hookent execve pour substituer /bin/ps par une version filtrée, ou pour lancer un binaire caché sur certains arguments ; comparer la sortie du tracepoint à /proc/<pid>/exe est la vérification standard. Pour la sécurité conteneur, un execve inattendu depuis un pod de charge mérite presque toujours une alerte.

Erreurs

E2BIG
La combinaison argv + envp + vecteur auxiliaire dépasse la limite ARG_MAX du noyau (généralement 128 KiB, ou 1/4 de RLIMIT_STACK sur les noyaux récents).
EACCES
Permission de recherche refusée sur un composant, ou pathname non exécutable, ou système de fichiers monté noexec.
EFAULT
—
EINVAL
—
EIO
—
EISDIR
—
ELIBBAD
Un interpréteur ELF (par ex. ld.so) référencé dans le binaire n'a pas pu être chargé.
ELOOP
Trop de niveaux de liens symboliques ou d'interpréteurs ont été traversés.
EMFILE
—
ENAMETOOLONG
—
ENFILE
—
ENOENT
—
ENOEXEC
Le fichier est reconnu mais n'est pas dans un format exécutable supporté (architecture incorrecte, ELF corrompu).
ENOMEM
—
ENOTDIR
—
EPERM
Le système de fichiers est monté nosuid et le binaire est set-UID/GID, ou no_new_privs bloque l'élévation de privilèges.
ETXTBSY
Le binaire est actuellement ouvert en écriture par un autre processus.

Syscalls liés