Skip to content
/linux-syscalls

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

ArchitectureNuméroABIPoint d'entrée
x86 (i386)331i386sys_pipe2
x64 (x86_64)293commonsys_pipe2
ARM64 (aarch64)59—sys_pipe2

Historique noyau

Introduit dans Linux 2.6.27.

  1. 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é.

  2. 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)                = 0

pipe2() 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.

Syscalls liés