IPC · Section 2
pipe2(2)
Crée un tube unidirectionnel et positionne atomiquement O_CLOEXEC / O_NONBLOCK sur les descripteurs résultants.
Signature
#include <unistd.h>
int pipe2(int [2] pipefd, int flags);- pipefd
- Paramètre de sortie : tableau int à 2 éléments. Le noyau y écrit l'extrémité lecture en pipefd[0] et l'extrémité écriture en pipefd[1].
- flags
- OU bit-à-bit de O_CLOEXEC (fermer à exec), O_NONBLOCK (E/S non bloquante), O_DIRECT (mode paquet — chaque écriture devient une lecture distincte).
Description
pipe2() crée un canal de données unidirectionnel — une paire de descripteurs où les données écrites sur pipefd[1] (extrémité d'écriture) sont lues depuis pipefd[0] (extrémité de lecture), en ordre FIFO. Le noyau tamponne jusqu'à /proc/sys/fs/pipe-max-size octets (défaut 1 MiB ; taille ajustable par fcntl(F_SETPIPE_SZ)). Quand le dernier descripteur d'écriture est fermé, les lectures suivantes retournent 0 (EOF) ; quand le dernier descripteur de lecture est fermé, les écritures lèvent SIGPIPE et retournent -1/EPIPE. L'argument flags permet de positionner O_CLOEXEC et/ou O_NONBLOCK atomiquement à la création — c'est la raison d'être de pipe2() ; l'original pipe() imposait un fcntl() séparé qui pouvait courir avec un fork+exec concurrent.
Numéros par architecture
| Architecture | Numéro | ABI | Point d'entrée |
|---|---|---|---|
| x86 (i386) | 331 | i386 | sys_pipe2 |
| x64 (x86_64) | 293 | common | sys_pipe2 |
| ARM64 (aarch64) | 59 | — | sys_pipe2 |
Historique noyau
Introduit dans Linux 2.6.27.
2.6.27
pipe2() a été ajouté en 2.6.27 avec la famille CLOEXEC-atomique (accept4, dup3, SOCK_CLOEXEC, signalfd4…) pour fermer la course historique où fcntl(F_SETFD, FD_CLOEXEC) après un syscall pouvait être observé en pleine execve par un frère forké.
3.4
O_DIRECT a été ajouté pour les tubes en mode paquet (pas de fusion des frontières de message). Utile pour des protocoles comme dbus qui veulent que chaque write() soit une read() distincte.
seccomp & conteneurs
Docker default profile
Autorisé
Podman default profile
Autorisé
pipe() et pipe2() sont dans tout profil par défaut. En pratique impossibles à bloquer — chaque pipeline shell, chaque capture asynchrone de stdout, chaque tube de signal de superviseur en dépend. Pas de filtrage d'arguments utile.
libseccomp
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pipe), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pipe2), 0);Exemple strace
$ strace -e pipe2 bash -c 'echo a | wc -c'
pipe2([3, 4], O_CLOEXEC) = 0pipe2() retourne les deux descripteurs en tableau — strace les affiche [3, 4]. Suivre les deux extrémités avec -y et -e read,write,pipe2 reconstruit tout le trafic du tube. Un programme avec tubes bloqués apparaît comme un écrivain bloqué sur write(pipefd[1], …) sans read() correspondant — diagnostic fréquent des pipelines shell.
Sécurité & observabilité
pipe() / pipe2() sont au cœur de CVE-2022-0847 « Dirty Pipe » — un bug noyau où le chemin splice fusionnait mal des pages de tube dans le cache de pages, permettant à un utilisateur non privilégié de réécrire des fichiers en lecture seule. Correctif en 5.16.11/5.15.25/5.10.102. Patché dans tout noyau de mi-2022 ; vérifier /proc/version. Au-delà, les tubes sont rarement un signal direct — trop universels pour superviser utilement. Pour la forensique conteneur, l'inspection de /proc/<pid>/fd montre les inodes de tubes croisables avec le processus pair pour cartographier la topologie IPC.
Erreurs
- EFAULT
- pipefd pointe hors de l'espace d'adressage du processus.
- EINVAL
- Drapeaux invalides.
- EMFILE
- Limite par processus de fd atteinte (chaque tube consomme deux descripteurs).
- ENFILE
- Limite système de fd atteinte.
Drapeaux
- O_CLOEXEC
- 02000000
- Positionne close-on-exec sur les deux descripteurs. Indispensable — tout tube interne qui ne doit pas fuir à un enfant doit avoir CLOEXEC.
- O_NONBLOCK
- 04000
- Les deux extrémités deviennent non bloquantes. read() retourne EAGAIN si vide ; write() retourne EAGAIN si le tampon est plein.
- O_DIRECT
- 040000
- Tube en mode paquet (depuis 3.4). Chaque write() devient une frontière distincte de read() — utile pour des protocoles à enregistrements où il ne faut pas que le noyau fusionne.