memfd_create(2) System Calls Manual memfd_create(2)

memfd_create - Créer un fichier anonyme

Bibliothèque C standard (libc, -lc)

#define _GNU_SOURCE         /* Consultez feature_test_macros(7) */
#include <sys/mman.h>
int memfd_create(const char *name, unsigned int flags);

memfd_create() crée un fichier anonyme et renvoie un descripteur de fichier qui s'y rapporte. Le fichier se comporte comme un fichier normal, il peut donc être modifié, tronqué, projeté en mémoire, et ainsi de suite. Mais contrairement à un fichier normal, il réside dans la RAM et son stockage est volatile. Une fois que toutes les références au fichier ont disparu, il est automatiquement libéré. La mémoire anonyme est utilisée pour toutes les pages de sauvegarde du fichier. Les fichiers créés par memfd_create() ont donc la même sémantique que les autres allocations de mémoire anonyme telles que celles qui utilisent mmap(2) avec l'attribut MAP_ANONYMOUS.

La taille initiale du fichier est positionnée à 0. Après l'appel, elle devrait être définie en utilisant ftruncate(2) (ou le fichier peut être rempli par des appels à write(2) ou équivalent).

Le nom fourni dans name est utilisé comme nom de fichier et sera affiché en tant que cible du lien symbolique correspondant dans le répertoire /proc/self/fd/. Le nom affiché a toujours un préfixe memfd: et il ne sert que pour le débogage. Les noms ne changent pas le comportement du descripteur de fichier et en tant que tels plusieurs fichiers peuvent avoir le même nom sans effets de bord.

Les valeurs suivantes peuvent subir une opération OU logique bit à bit dans flags pour modifier le comportement de memfd_create() :

Placer l'attribut « close-on-exec » (FD_CLOEXEC) sur le nouveau descripteur de fichier. Consultez la description de l'attribut O_CLOEXEC dans open(2) pour savoir pourquoi cela peut être utile.
Permettre des opérations de verrouillage sur ce fichier. Voir le point sur les opérations F_ADD_SEALS et F_GET_SEALS dans fcntl(2), ainsi que les NOTES ci-dessous. Le positionnement initial des verrous est vide. Si cet attribut n'est pas défini, le positionnement initial des verrous sera F_SEAL_SEAL, ce qui veut dire qu'aucun autre verrou ne peut être positionné sur le fichier.
Le fichier anonyme sera créé sur le système de fichiers hugetlbfs en utilisant d'immenses pages. Voir le fichier Documentation/admin-guide/mm/hugetlbpage.rst des sources du noyau Linux pour plus d'informations sur hugetlbfs. Le fait d'indiquer à la fois MFD_HUGETLB et MFD_ALLOW_SEALING dans flags est pris en charge depuis Linux 4.16.
...
Utilisé avec MFD_HUGETLB pour sélectionner d'autres tailles de page hugetlb (respectivement 2 Mo, 1 Go, ...) sur les systèmes qui gèrent plusieurs tailles de page hugetlb. Les définitions des tailles de page immenses connues figurent dans le fichier d'entête <linux/memfd.h>.
Pour des détails sur l'encodage des tailles des pages immenses ne figurant pas dans le fichier d'entête, voir le point sur les constantes du même nom dans mmap(2).

Les bits inusitées dans flags doivent valoir 0.

En code de retour, memfd_create() renvoie un nouveau descripteur de fichier qui peut être utilisé pour se référer au fichier. Ce descripteur de fichier est ouvert en lecture et en écriture (O_RDWR) et O_LARGEFILE est positionné pour le descripteur de fichier.

Par rapport à fork(2) et execve(2), la sémantique habituelle s'applique au descripteur de fichier créé par memfd_create(). Une copie du descripteur de fichier est récupérée par l'enfant produit par fork(2) et elle se rapporte au même fichier. Le descripteur de fichier est préservé pendant un execve(2), sauf si l'attribut close-on-exec a été positionné.

En cas de succès, memfd_create() renvoie un nouveau descripteur de fichier. En cas d'erreur, -1 est renvoyé et errno est positionné pour indiquer l'erreur.

L'adresse dans name pointe vers une mémoire non valable.
flags comprend des bits inconnus.
name était trop long (la limite de 249 octets, n'incluant pas l'octet NULL final).
MFD_HUGETLB et MFD_ALLOW_SEALING ont tous deux été indiqués dans flags.
La limite du nombre de descripteurs de fichiers par processus a été atteinte.
La limite du nombre total de fichiers ouverts pour le système entier a été atteinte.
Mémoire insuffisante pour créer un nouveau fichier anonyme.
L'attribut MFD_HUGETLB a été spécifié, mais l'appelant n'est pas un utilisateur privilégié (il n'a pas la capacité CAP_IPC_LOCK) et il n'est pas membre du groupe sysctl_hugetlb_shm_group ; consultez la description de /proc/sys/vm/sysctl_hugetlb_shm_group dans proc(5).

Linux.

Linux 3.17, glibc 2.27.

L'appel système memfd_create() offre une alternative simple au montage manuel d'un système de fichiers tmpfs(5) et à la création et l'ouverture d'un fichier dans ce système de fichiers. Le premier objectif de memfd_create() est de créer des fichiers et leur descripteur associé, utilisés avec les API de verrou de fichiers fournis par fcntl(2).

L'appel système memfd_create() s'utilise également sans verrou de fichier (c'est pourquoi le verrouillage de fichier a été désactivé sauf demande explicite avec l'attribut MFD_ALLOW_SEALING). En particulier, il peut être utilisé comme alternative pour créer des fichiers dans tmp ou pour utiliser O_TMPFILE de open(2), si vous ne voulez pas rattacher le fichier résultant au système de fichiers.

En l'absence de verrou de fichier, les processus qui communiquent à travers la mémoire partagée doivent soit se faire confiance entre eux, soit prendre des mesures pour gérer la possibilité qu'un pair non fiable manipule la région de mémoire partagée de manière problématique. Par exemple, un pair non fiable pourrait modifier le contenu de la mémoire partagée n'importe quand ou rétrécir la zone de mémoire partagée. La première éventualité rend le processus local vulnérable aux conflits (race conditions) time-of-check-to-time-of-use (généralement gérés en copiant les données de la zone de mémoire partagée avant de les vérifier et de les utiliser). La deuxième éventualité rend le processus local vulnérable aux signaux SIGBUS quand on essaie d'accéder à un emplacement inexistant dans la zone de mémoire partagée (gérer cette éventualité implique d'utiliser un gestionnaire pour le signal SIGBUS).

La gestion de pairs non fiables impose une plus grande complexité du code qui utilise la mémoire partagée. Les verrous mémoire éliminent cette complexité, en permettant à un processus d'agir en toute sécurité en sachant que son pair ne peut pas modifier la mémoire partagée de manière non souhaitée.

Voici un exemple d'utilisation du mécanisme de verrouillage :

(1)
Le premier processus crée un fichier tmpfs(5) en utilisant memfd_create(). L'appel donne un descripteur de fichier utilisé dans les étapes ultérieures.
(2)
Le premier processus dimensionne le fichier créé à l'étape précédente en utilisant ftruncate(2), il le projette en utilisant mmap(2) et il remplit la mémoire partagée avec les données désirées.
(3)
Le premier processus utilise l'opération F_ADD_SEALS de fcntl(2) pour poser un ou plusieurs verrous sur le fichier afin de restreindre des modifications ultérieures (si on pose un verrou F_SEAL_WRITE, il sera nécessaire de désassocier la projection modifiable partagée créée à l'étape précédente. Sinon, on peut obtenir un comportement identique à F_SEAL_WRITE en utilisant F_SEAL_FUTURE_WRITE, qui empêchera des écritures ultérieures à l'aide de mmap(2) et de write(2), tout en conservant les projections modifiables partagées existantes).
(4)
Un deuxième processus obtient un descripteur de fichier pour le fichier tmpfs(5) et le projette. Parmi les origines possibles de cela, vous trouverez :
  • Le processus qui a appelé memfd_create() a pu transférer le descripteur de fichier consécutif au deuxième processus à l'aide d'un socket de domaine UNIX (voir unix(7) et cmsg(3)). Le deuxième processus projette alors le fichier en utilisant mmap(2).
  • Le deuxième processus est créé à l'aide de fork(2) et, ainsi, il récupère automatiquement le descripteur de fichier et sa projection (remarquez que dans ce cas et dans le prochain, il existe une relation de confiance naturelle entre les deux processus puisqu'ils tournent sous le même identifiant utilisateur. Donc, un verrou de fichier n'est, en principe, pas nécessaire).
  • Le deuxième processus ouvre le fichier /proc/pid/fd/fd où <pid> est l'identifiant de processus du premier processus (celui qui a appelé memfd_create()) et <fd> est le numéro du descripteur de fichier renvoyé par l'appel à memfd_create dans ce processus. Le deuxième processus projette ensuite le fichier en utilisant mmap(2).
(5)
Le deuxième processus utilise l'opération F_GET_SEALS de fcntl(2) pour récupérer le masque de bits de verrous appliqué au fichier. Ce masque peut être examiné pour déterminer le type de restrictions posées aux modifications du fichier. Si vous le souhaitez, le deuxième processus peut appliquer des verrous supplémentaires pour imposer d'autres restrictions (tant que le verrou F_SEAL_SEAL n'a pas encore été appliqué).

Voici deux exemples de programme montrant l'utilisation de memfd_create() et de l'API de verrou de fichier.

Le premier programme, t_memfd_create.c, crée un fichier tmpfs(5) en utilisant memfd_create(), donne une taille au fichier, le projette en mémoire et, en option, pose des verrous sur le fichier. Le programme accepte jusqu'à trois paramètres en ligne de commande, dont les deux premiers sont requis. Le premier paramètre est le nom à donner au fichier, le deuxième est la taille à lui donner, le troisième, optionnel, est une chaîne de caractères qui indique les verrous à poser sur le fichier.

Le deuxième programme, t_get_seals.c, peut être utilisé pour ouvrir un fichier existant créé à l'aide de memfd_create() et examiner les verrous qui y sont posés.

La session d'interpréteur suivant montre l'utilisation de ces programmes. Nous créons d'abord un fichier tmpfs(5) et nous posons des verrous dessus :


$ ./t_memfd_create my_memfd_file 4096 sw &
[1] 11775
PID: 11775; fd: 3; /proc/11775/fd/3

À ce moment, le programme t_memfd_create continue à s'exécuter en tâche de fond. À partir d'un autre programme, nous pouvons obtenir un descripteur de fichier pour le fichier créé par memfd_create() en ouvrant /proc/pid/fd qui correspond au descripteur de fichier ouvert par memfd_create(). En utilisant ce chemin, nous examinons le contenu du lien symbolique /proc/pid/fd et nous utilisons notre programme t_get_seals pour voir les verrous posés sur le fichier :


$ readlink /proc/11775/fd/3
/memfd:my_memfd_file (deleted)
$ ./t_get_seals /proc/11775/fd/3
Verrous existants : WRITE SHRINK

Source du programme : t_memfd_create.c

#define _GNU_SOURCE
#include <err.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
    int           fd;
    char          *name, *seals_arg;
    ssize_t       len;
    unsigned int  seals;
    if (argc < 3) {
        fprintf(stderr, "%s name size [seals]\n", argv[0]);
        fprintf(stderr, "\t'seals' peut contenir un des "
                "caractères suivants :\n");
        fprintf(stderr, "\t\tg - F_SEAL_GROW\n");
        fprintf(stderr, "\t\ts - F_SEAL_SHRINK\n");
        fprintf(stderr, "\t\tw - F_SEAL_WRITE\n");
        fprintf(stderr, "\t\tW - F_SEAL_FUTURE_WRITE\n");
        fprintf(stderr, "\t\tS - F_SEAL_SEAL\n");
        exit(EXIT_FAILURE);
    }
    name = argv[1];
    len = atoi(argv[2]);
    seals_arg = argv[3];
    /* Créer un fichier anonyme dans tmpfs ; permet de poser
        des verrous sur le fichier. */
    fd = memfd_create(name, MFD_ALLOW_SEALING);
    if (fd == -1)
        err(EXIT_FAILURE, "memfd_create");
    /* Taille du fichier indiquée sur la ligne de commande. */
    if (ftruncate(fd, len) == -1)
        err(EXIT_FAILURE, "truncate");
    printf("PID: %jd; fd: %d; /proc/%jd/fd/%d\n",
           (intmax_t) getpid(), fd, (intmax_t) getpid(), fd);
    /* Code pour projeter le fichier et remplir la projection
       avec des données omises. */
    /* Si un paramètre 'seals' de la ligne de commande est fourni,
       poser des verrous sur le fichier. */
    if (seals_arg != NULL) {
        seals = 0;
        if (strchr(seals_arg, 'g') != NULL)
            seals |= F_SEAL_GROW;
        if (strchr(seals_arg, 's') != NULL)
            seals |= F_SEAL_SHRINK;
        if (strchr(seals_arg, 'w') != NULL)
            seals |= F_SEAL_WRITE;
        if (strchr(seals_arg, 'W') != NULL)
            seals |= F_SEAL_FUTURE_WRITE;
        if (strchr(seals_arg, 'S') != NULL)
            seals |= F_SEAL_SEAL;
        if (fcntl(fd, F_ADD_SEALS, seals) == -1)
            err(EXIT_FAILURE, "fcntl");
    }
    /* Continuer l’exécution pour que le fichier créé par
       memfd_create() continue à exister. */
    pause();
    exit(EXIT_SUCCESS);
}

Source du programme : t_get_seals.c

#define _GNU_SOURCE
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
    int           fd;
    unsigned int  seals;
    if (argc != 2) {
        fprintf(stderr, "%s /proc/PID/fd/FD\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_RDWR);
    if (fd == -1)
        err(EXIT_FAILURE, "open");
    seals = fcntl(fd, F_GET_SEALS);
    if (seals == -1)
        err(EXIT_FAILURE, "fcntl");
    printf("Verrous existants :");
    if (seals & F_SEAL_SEAL)
        printf(" SEAL");
    if (seals & F_SEAL_GROW)
        printf(" GROW");
    if (seals & F_SEAL_WRITE)
        printf(" WRITE");
    if (seals & F_SEAL_FUTURE_WRITE)
        printf(" FUTURE_WRITE");
    if (seals & F_SEAL_SHRINK)
        printf(" SHRINK");
    printf("\n");
    /* Code pour associer le fichier et l'accès au contenu de la
       projection résultante omise. */
    exit(EXIT_SUCCESS);
}

fcntl(2), ftruncate(2), memfd_secret(2), mmap(2), shmget(2), shm_open(3)

La traduction française de cette page de manuel a été créée par Christophe Blaess https://www.blaess.fr/christophe/, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Philippe MENGUAL <jpmengual@debian.org>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.

2 mai 2024 Pages du manuel de Linux 6.8