MEMFD_CREATE(2)

名前
memfd_create - 無名ファイル (anonymous file) を作成する

書式
#define _GNU_SOURCE         /* feature_test_macros(7) 参照 */
#include <sys/mman.h>

int memfd_create(const char *name, unsigned int flags);

説明
memfd_create() は、 無名ファイル (anonymous file) を作成し、 そのファイルを参照するファイルディスクリプターを返す。 このファイルは通常のファイルと同様に振る舞い、 変更、切り詰め (truncate)、 メモリーマップなどを行うことができる。 しかし、 通常のファイルとは違い、 このファイルは RAM 上に置かれ、 格納されるストレージは揮発性である。 このファイルへの参照がすべてなくなると、 ファイルは自動的に解放される。 このファイルが置かれるページには無名メモリー (anonymous memory) が使用される。 したがって、 memfd_create() で作成されたファイルは、 他の無名メモリーの割り当て (MAP_ANONYMOUS フラグ付きの mmap(2) を使って割り当てられた無名メモリーなど) と同じ動作をする。

ファイルの初期サイズは 0 に設定される。 呼び出しの後に、 ftruncate(2) を使ってファイルサイズを設定すべきである (代わりに、 write(2) や同様の関数を呼び出してファイルにデータを書き込むこともできる)。

name に指定された名前はファイル名として使用され、 ディレクトリ /proc/self/fd/ で対応するシンボリックリンクのリンク先として表示される。 表示される名前の前には常に memfd: が付き、 この名前はデバッグ用途としてのみ機能する。 名前はファイルディスクリプターの動作には影響せず、 複数のファイルが同じ名前を持っても副作用はない。

以下の値をビット論理和で flags に指定して、 memfd_create() の動作を変更できる。

MFD_CLOEXEC
    新しいファイルディスクリプターに close-on-exec (FD_CLOEXEC) フラグをセットする。 これが有用な理由については open(2) の O_CLOEXEC フラグの説明を参照のこと。

MFD_ALLOW_SEALING
    このファイルに対して sealing 操作を許可する。 fcntl(2) の F_ADD_SEALS と F_GET_SEALS 操作の議論を参照。 下記の「注意」も参照。 初期の seal 集合は空となる。 このフラグを指定しなかった場合、 初期の seal 集合は F_SEAL_SEAL となり、 これはこのファイルには他の seal をセットできないことということである。

MFD_HUGETLB (Linux 4.14 以降)
    The anonymous file will be created in the hugetlbfs filesystem using huge pages. See the Linux kernel source file Documentation/admin-guide/mm/hugetlbpage.rst for more information about hugetlbfs. Specifying both MFD_HUGETLB and MFD_ALLOW_SEALING in flags is supported since Linux 4.16.

MFD_HUGE_2MB, MFD_HUGE_1GB, ...
    Used in conjunction with MFD_HUGETLB to select alternative hugetlb page sizes (respectively, 2 MB, 1 GB, ...) on systems that support multiple hugetlb page sizes. Definitions for known huge page sizes are included in the header file <linux/memfd.h>.

    For details on encoding huge page sizes not included in the header file, see the discussion of the similarly named constants in mmap(2).

flags の未使用のビットは 0 でなければならない。

返り値として memfd_create() は、 作成したファイルを参照するのに使用できる新しいファイルディスクリプターを返す。 このファイルディスクリプターは読み書き両用 (O_RDWR) でオープンされ、 O_LARGEFILE がこのファイルディスクリプターにセットされる。

fork(2) と execve(2) に関しては、 memfd_create() で作成したファイルディスクリプターについても通常の動作が適用される。 ファイルディスクリプターのコピーは fork(2) で生成される子プロセスに継承され、 同じファイルを参照する。 close-on-exec フラグがセットされていない限り、 execve(2) の前後でファイルディスクリプターは保持される。

返り値
成功の場合、 memfd_create() は新しいファイルディスクリプターを返す。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。

エラー

EFAULT
    name のアドレスが無効なメモリーを指している。

EINVAL
    flags included unknown bits.

EINVAL
    name was too long. (The limit is 249 bytes, excluding the terminating null byte.)

EINVAL
    Both MFD_HUGETLB and MFD_ALLOW_SEALING were specified in flags.

EMFILE
    オープンされているファイルディスクリプター数のプロセス単位の上限に達した。

ENFILE
    システム全体でオープンされているファイルの総数が上限に達した。

ENOMEM
    新しい無名ファイルを作成するのに十分なメモリーがなかった。

バージョン
memfd_create() システムコールは Linux 3.17 で登場した。 glibc でのサポートは glibc バージョン 2.27 で追加された。

準拠
memfd_create() システムコールは Linux 固有である。

注意
memfd_create() システムコールは、 手動で tmpfs(5) ファイルシステムをマウントして、 そのファイルシステムにファイルをオープンするという操作の、 簡単な代替手段を提供している。 memfd_create() の主な目的は、 fcntl(2) が提供する file-sealing API で使用できる、 ファイルとそれに関連付けられるファイルディスクリプターを作成することである。

memfd_create() システムコールは、 file sealing なしでも用途がある (これが明示的に MFD_ALLOW_SEALING フラグが要求されない限り、 file-sealing が無効になる理由である)。 特に、 ファイルシステムに実際にファイルを残す意図がない場合、 tmp にファイルを作成したり open(2) O_TMPFILE を使ったりする際の代替手段として使用できる。

  file sealing
file sealing がない場合、 共有メモリー経由で通信するプロセスは、 互いに信頼するか、 信頼していない相手が共有メモリー領域を問題がある方法で操作する可能性に対処するための対策を講じなければならない。 例えば、 信頼していない相手は、 いつでも共有メモリーの内容を変更したり、 共有メモリー領域を縮小したりする可能性がある。 前者の場合は、 ローカルプロセスでは、 データの確認時点と使用時点の競合条件の問題が起こり得る (通常はこの問題への対処は共有メモリー領域からデータをこぴーしてからデータを確認、使用することである)。 後者の場合は、 ローカルプロセスでは、 共有メモリー領域の存在しなくなった場所にアクセスしようとした際にシグナル SIGBUS が発生する可能性がある (この可能性に対処するにはシグナル SIGBUS に対してハンドラーを使用する必要がある)。

信頼していない相手への対処により、 共有メモリーを利用するコードに余計な複雑性が増すことになる。 メモリー sealing により余計な複雑性をなくすことができる。 相手が望まない方法で共有メモリーを変更できないことを知っていることで、 プロセスは安全に動作できるようになる。

sealing 機構の使い方の例は以下のとおりである。

1.  最初のプロセスは memfd_create() を使って tmpfs(5) ファイルを作成する。 memfd_create() はこれ以降のステップで使用するファイルディスクリプターを返す。

2.  最初のプロセスは ftruncate(2) を使って直前のステップで作成したファイルのサイズを変更し、 mmap(2) を使ってそのファイルをマッピングし、 共有メモリーに所望のデータを配置する。

3.  The first process uses the fcntl(2) F_ADD_SEALS operation to place one or more seals on the file, in order to restrict further modifications on the file. (If placing the seal F_SEAL_WRITE, then it will be necessary to first unmap the shared writable mapping created in the previous step. Otherwise, behavior similar to F_SEAL_WRITE can be achieved by using F_SEAL_FUTURE_WRITE, which will prevent future writes via mmap(2) and write(2) from succeeding while keeping existing shared writable mappings).

4.  二つ目のプロセスは tmpfs(5) ファイルのファイルディスクリプターを入手し、 そのファイルをマップする。 以下に示す方法を使用することができる。

    *   memfd_create() を呼び出したプロセスは、 得られたファイルディスクリプターを二つ目のプロセスに UNIX ドメインソケット経由で渡すことができる (unix(7) と cmsg(3) を参照)。 それから、二つ目のプロセスは mmap(2) を使ってファイルをマップする。

    *   二つ目のプロセスを fork(2) を使って作成する。 そうすると、 自動的にファイルディスクリプターとマッピングが継承される。 (この方法と次の方法では、 二つのプロセス間で自然な信頼関係が存在することになる。 なぜなら、 二つのプロセスは同じユーザー ID。 の元で実行されているからである。 したがって、 file sealing は通常は不要であろう。)

    *   二つ目のプロセスは /proc/<pid>/fd/<fd> をオープンする。 <pid> は最初のプロセス (memfd_create() を呼び出したプロセス) の PID で、 <fd> は最初のプロセスでの memfd_create() の呼び出しで返されたファイルディスクリプター番号である。 それからこのファイルを mmap(2) を使ってマッピングする。

5.  二つ目のプロセスは fcntl(2) の F_GET_SEALS 操作を使って、 そのファイルに適用されている seal のビットマスクを取得する。 このビットマスクを調べて、 ファイルの変更に関してどのような制限が適用されているかを知ることができる。 (F_SEAL_SEAL seal がそれまでに適用されていない限りは) 必要であれば、 二つ目のプロセスはさらに seal を設定して追加の制限をかけることができる。

例
以下では memfd_create() と file sealing API の使用例を示すサンプルプログラムを 2 つとりあげる。

最初のプログラム t_memfd_create.c は、 memfd_create() を使って tmpfs(5) ファイルを作成し、 そのファイルのサイズを設定し、 メモリーにマッピングし、 要求された場合にはそのファイルに seal を設定する。 このプログラムは最大で 3 つのコマンドライン引数を取り、 最初の 2 つは必須である。 最初の引数はファイルに関連付けられる名前で、 2 番目の引数はファイルに設定されるサイズである。 省略可能な 3 番目の引数は、 このファイルに設定する seal を指定する文字列である。

2 つめのプログラム t_get_seals.c を使うと、 memfd_create() を使って作成された既存のファイルをオープンし、 そのファイルに適用されている seal の集合を調査できる。

以下のシェルのセッションはこれらのプログラムの使用例を示したものである。 まず tmpfs(5) ファイルを作成し、そのファイルに seal をいくつか設定している。

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

この時点では、 t_memfd_create プログラムはバックグラウンドで動作し続ける。 もう一つのプログラムから、 memfd_create() がオープンしたファイルディスクリプターに対応する /proc/[pid]/fd ファイルをオープンすることで、 memfd_create() で作成されたファイルのファイルディスクリプターを取得できる。そのパス名を使って、 /proc/[pid]/fd シンボリックリンクの内容を調査し、 t_get_seals プログラムを使ってそのファイルに設定されている seal を見ることができる。

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

  プログラムのソース: t_memfd_create.c
    #define _GNU_SOURCE
    #include <stdint.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>

    #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                            } while (0)

    int
    main(int argc, char *argv[])
    {
        int fd;
        unsigned int seals;
        char *addr;
        char *name, *seals_arg;
        ssize_t len;

        if (argc < 3) {
            fprintf(stderr, "%s name size [seals]\n", argv[0]);
            fprintf(stderr, "\t'seals' can contain any of the "
                    "following characters:\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];

        /* Create an anonymous file in tmpfs; allow seals to be
           placed on the file */

        fd = memfd_create(name, MFD_ALLOW_SEALING);
        if (fd == -1)
            errExit("memfd_create");

        /* Size the file as specified on the command line */

        if (ftruncate(fd, len) == -1)
            errExit("truncate");

        printf("PID: %jd; fd: %d; /proc/%jd/fd/%d\n",
                (intmax_t) getpid(), fd, (intmax_t) getpid(), fd);

        /* Code to map the file and populate the mapping with data
           omitted */

        /* If a 'seals' command-line argument was supplied, set some
           seals on the file */

        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)
                errExit("fcntl");
        }

        /* Keep running, so that the file created by memfd_create()
           continues to exist */

        pause();

        exit(EXIT_SUCCESS);
    }

  プログラムのソース: t_get_seals.c
    #define _GNU_SOURCE
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>

    #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                            } while (0)

    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)
            errExit("open");

        seals = fcntl(fd, F_GET_SEALS);
        if (seals == -1)
            errExit("fcntl");

        printf("Existing seals:");
        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 to map the file and access the contents of the
           resulting mapping omitted */

        exit(EXIT_SUCCESS);
    }

関連項目
fcntl(2), ftruncate(2), mmap(2), shmget(2), shm_open(3)

この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。