Skip to content
/linux-syscalls

Processus & Threads · Section 2

clone(2)

Crée un nouveau processus qui peut partager sélectivement mémoire, descripteurs, espaces de noms et autres ressources avec son parent.

Signature

#include <sched.h>

long clone(int (*)(void *) fn, void * stack, int flags, void * arg, pid_t * parent_tid, void * tls, pid_t * child_tid);
fn
Fonction d'entrée exécutée dans l'enfant (wrapper glibc). L'appel système brut ne prend pas de pointeur de fonction.
stack
Pointeur vers l'octet le plus haut de la pile enfant. L'enfant s'exécute sur cette pile ; pour les processus (sans CLONE_VM), passer NULL.
flags
Masque de bits CLONE_* OU avec le signal de terminaison envoyé au parent (généralement SIGCHLD).
arg
Argument transmis à fn dans l'enfant (wrapper glibc uniquement).
parent_tid
Si CLONE_PARENT_SETTID, le noyau écrit ici le TID enfant, visible par le parent.
tls
Descripteur de stockage local au thread utilisé avec CLONE_SETTLS ; spécifique à l'architecture.
child_tid
Si CLONE_CHILD_SETTID, écrit dans l'espace d'adressage enfant ; si CLONE_CHILD_CLEARTID, effacé et signalé via futex à la sortie (réveil de pthread_join).

Description

clone() est la primitive Linux derrière fork(), pthread_create() et tout runtime de conteneur. Contrairement à fork() POSIX, la nouvelle tâche peut partager sélectivement l'espace d'adressage du parent (CLONE_VM), la table des descripteurs (CLONE_FILES), le contexte du système de fichiers (CLONE_FS), les gestionnaires de signaux (CLONE_SIGHAND) et les espaces de noms (CLONE_NEWPID, CLONE_NEWNET, CLONE_NEWUSER…). Avec CLONE_THREAD plus CLONE_VM|CLONE_SIGHAND elle produit un thread ; sans ces drapeaux, un processus. clone() renvoie le TID de l'enfant au parent et 0 dans l'enfant, ou -1 avec errno. Le wrapper glibc prend un pointeur de fonction fn comme point d'entrée enfant ; l'appel système brut en assembleur a une structure différente, détaillée à la rubrique « C library/kernel differences » du man.

Numéros par architecture

ArchitectureNuméroABIPoint d'entrée
x86 (i386)120i386sys_clone
x64 (x86_64)56commonsys_clone
ARM64 (aarch64)220—sys_clone

Historique noyau

Introduit dans Linux 2.0.

  1. 2.0

    clone() a été introduit en 2.0 comme généralisation Linux de fork(), permettant les threads avec espaces d'adressage partagés — la brique sur laquelle NPTL a été bâti.

  2. 2.4.20

    CLONE_NEWNS a introduit les espaces de montage, première des primitives d'espaces de noms Linux étendues plus tard en la pleine boîte à outils des conteneurs.

  3. 2.6.24

    CLONE_NEWPID et CLONE_NEWUSER ont complété l'ensemble des espaces de noms nécessaires aux conteneurs complets ; les espaces utilisateur ont rendu possibles les conteneurs rootless.

  4. 5.3

    clone3() a été ajouté avec une struct clone_args passée par référence, étendant flags au-delà de la limite des 32 bits de registre et permettant d'ajouter des champs futurs sans nouveaux appels système.

seccomp & conteneurs

Docker default profile

Autorisé

Podman default profile

Autorisé

clone() figure dans les profils par défaut Docker / Podman, mais les drapeaux de création d'espaces de noms (CLONE_NEWUSER, CLONE_NEWNET, CLONE_NEWPID, CLONE_NEWNS…) sont filtrés par les profils via filtrage d'arguments. Les autoriser dans un conteneur permet l'imbrication de conteneurs — et permet à un conteneur compromis de construire un nouvel espace utilisateur en PID 1 puis d'y devenir root. Pour des charges qui n'ont pas à lancer de conteneur, refuser explicitement le jeu de drapeaux de création d'espace de noms.

libseccomp

// Allow clone but deny namespace-creation flags (defence-in-depth for containers)
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone),
    1, SCMP_A0(SCMP_CMP_MASKED_EQ,
               CLONE_NEWUSER|CLONE_NEWNET|CLONE_NEWPID|CLONE_NEWNS, 0));

Exemple strace

$ strace -f -e clone /bin/sh -c 'true'
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb2a1c7da10) = 14982
[pid 14982] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=14982, si_uid=1000, si_status=0, …} ---

strace -f suit automatiquement les enfants clone() ; la sortie décode le masque de bits flags. pthread_create apparaît comme clone(child_stack=…, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, …) — une empreinte utile pour distinguer création de thread vs de processus.

Sécurité & observabilité

clone() avec CLONE_NEWUSER est la base des primitives d'élévation par espace utilisateur — un processus qui obtient CLONE_NEWUSER devient root dans le nouvel espace et, sur des systèmes mal configurés, peut s'en servir pour s'évader. Le tracepoint eBPF sys_enter_clone avec arg0 (flags) est le point d'observation ; alerter sur le masque de création d'espace de noms. Les détections runtime de conteneurs (Falco, Tracee) s'y appuient. Certains rootkits utilisent aussi clone() pour forker des processus cachés partageant la table de fd parent (pas de nouveau /proc/<pid>/exe). Côté hôte, /proc/sys/kernel/unprivileged_userns_clone = 0 interdit la création d'espace utilisateur non privilégié sur les distributions Debian qui livrent ce levier.

Erreurs

EAGAIN
RLIMIT_NPROC atteint ou épuisement temporaire des ressources noyau.
EINVAL
Combinaison de drapeaux invalide (ex. CLONE_THREAD sans CLONE_VM ni CLONE_SIGHAND).
ENOMEM
Mémoire noyau insuffisante pour allouer les structures de tâche.
ENOSPC
—
EPERM
CLONE_NEWUSER demandé sans privilège (sauf si les espaces utilisateur sont activés pour les appelants non privilégiés), ou CLONE_NEW* sans CAP_SYS_ADMIN dans l'espace utilisateur parent.
EUSERS
—

Drapeaux

CLONE_VM
0x00000100
Partage l'espace d'adressage avec le parent — c'est ce qui fait de la nouvelle tâche un thread.
CLONE_FS
0x00000200
—
CLONE_FILES
0x00000400
Partage la table des descripteurs. Fermer un fd dans une tâche le ferme partout.
CLONE_SIGHAND
0x00000800
—
CLONE_PIDFD
0x00001000
Alloue un pidfd référant à l'enfant et l'écrit dans parent_tid (alternative moderne au signalement par PID).
CLONE_PTRACE
0x00002000
—
CLONE_VFORK
0x00004000
—
CLONE_PARENT
0x00008000
—
CLONE_THREAD
0x00010000
Place la nouvelle tâche dans le même groupe de threads (même TGID/PID) ; requiert CLONE_VM et CLONE_SIGHAND.
CLONE_NEWNS
0x00020000
Crée un nouvel espace de montage.
CLONE_SYSVSEM
0x00040000
—
CLONE_SETTLS
0x00080000
—
CLONE_PARENT_SETTID
0x00100000
—
CLONE_CHILD_CLEARTID
0x00200000
—
CLONE_CHILD_SETTID
0x01000000
—
CLONE_NEWCGROUP
0x02000000
—
CLONE_NEWUTS
0x04000000
—
CLONE_NEWIPC
0x08000000
—
CLONE_NEWUSER
0x10000000
Crée un nouvel espace utilisateur — base des conteneurs rootless.
CLONE_NEWPID
0x20000000
Crée un nouvel espace de PID ; l'enfant y devient PID 1.
CLONE_NEWNET
0x40000000
Crée un nouvel espace réseau avec ses propres interfaces et table de routage.
CLONE_IO
0x80000000
—

Syscalls liés