Processus & Threads · Section 2
fork(2)
Crée un nouveau processus en dupliquant le processus appelant.
Signature
#include <unistd.h>
pid_t fork();Description
fork() crée un nouveau processus — l'enfant — qui est une copie quasi-identique du processus appelant (le parent). L'enfant hérite de la mémoire du parent (copy-on-write), des descripteurs ouverts, des gestionnaires de signaux, du répertoire courant, de l'environnement et de la plupart des attributs ; il diffère par PID, PID parent, temps CPU cumulé (zéro), signaux en attente (aucun), verrous de fichier (non hérités) et quelques points plus subtils. En succès fork() retourne le PID enfant au parent et 0 dans l'enfant ; -1 avec errno en échec. Linux moderne implémente fork() via clone() avec SIGCHLD comme signal de sortie et sans drapeau de partage. Sur aarch64 l'appel système dédié fork() n'existe pas — la glibc implémente fork() via clone()/clone3().
Numéros par architecture
| Architecture | Numéro | ABI | Point d'entrée |
|---|---|---|---|
| x86 (i386) | 2 | i386 | sys_fork |
| x64 (x86_64) | 57 | common | sys_fork |
Historique noyau
Introduit dans Linux 1.0.
1.0
fork() est présent dans Linux depuis 1.0 avec la sémantique Unix classique.
2.0
À partir de 2.0, fork() est implémenté au-dessus de clone() — c'est un wrapper léger qui appelle clone(SIGCHLD, 0, …) en interne. La sémantique est inchangée mais le chemin noyau est partagé avec clone().
5.4
aarch64 (et la table d'appels asm-generic en général) n'expose pas d'appel système fork() — la glibc implémente fork() via clone()/clone3(). fork() en C reste portable ; un assembleur brut tentant __NR_fork sur aarch64 recevra ENOSYS.
seccomp & conteneurs
Docker default profile
Autorisé
Podman default profile
Autorisé
fork(), vfork() et clone() sont tous autorisés par les profils par défaut Docker / Podman. Bloquer fork() seul est inutile : la glibc retombera sur clone(). Pour empêcher réellement la création de processus, il faut bloquer clone()/clone3() (et sur i386, vfork()) ensemble. Après que init a lancé ses workers, un filtre seccomp qui refuse les trois est un solide durcissement contre la propagation post-exploitation.
libseccomp
// Allow process creation through fork/vfork/clone
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fork), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(vfork), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);Exemple strace
$ strace -f -e fork,clone /bin/sh -c '(true)'
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1a8c4ada10) = 14523
[pid 14523] +++ exited with 0 +++Sous Linux moderne, strace affiche clone() et non fork() dans la plupart des cas (c'est ce que la glibc appelle réellement). Utiliser -f pour suivre l'enfant, et -e fork,clone,vfork pour filtrer. La signature classique du fork-bomb en strace -c est un compte clone() élevé avec un temps par appel quasi nul.
Sécurité & observabilité
fork() est la base du 'fork bomb' classique — un processus qui se fork() en boucle épuise RLIMIT_NPROC et pid_max, gelant le système si ceux-ci ne sont pas posés. Sur les systèmes durcis, RLIMIT_NPROC et TasksMax= de systemd sont obligatoires. Au-delà du DoS, fork() est intéressant en sécurité conteneur car les nouveaux processus héritent des espaces de noms du parent — une compromission qui obtient exec mais pas la création d'espace de noms peut quand même pivoter via fork() pour lancer des workers. Le tracepoint eBPF sched_process_fork capture chaque fork (et clone) ; couplé à execve il donne le flux complet de création de processus.
Erreurs
- EAGAIN
- RLIMIT_NPROC de l'utilisateur atteint, ou limite système (nr_threads) épuisée, ou RLIMIT_NPROC de l'espace utilisateur atteint. Fréquent sous charge et symptôme classique du 'fork bomb'.
- ENOMEM
- Mémoire noyau insuffisante pour allouer la nouvelle task_struct, les tables de pages, ou la pile noyau.
- ENOSYS
- fork() n'est pas implémenté sur cette architecture (aarch64 moderne). La glibc convertit automatiquement en clone().
- ERESTARTNOINTR
- Valeur interne noyau — ne devrait jamais atteindre l'espace utilisateur. Si c'est le cas, suspecter un intercepteur d'appels système bogué.