xref: /qemu/util/memfd.c (revision dcff1035)
1f04cf923SMarc-André Lureau /*
2f04cf923SMarc-André Lureau  * memfd.c
3f04cf923SMarc-André Lureau  *
4f04cf923SMarc-André Lureau  * Copyright (c) 2015 Red Hat, Inc.
5f04cf923SMarc-André Lureau  *
6f04cf923SMarc-André Lureau  * QEMU library functions on POSIX which are shared between QEMU and
7f04cf923SMarc-André Lureau  * the QEMU tools.
8f04cf923SMarc-André Lureau  *
9f04cf923SMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
10f04cf923SMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
11f04cf923SMarc-André Lureau  * in the Software without restriction, including without limitation the rights
12f04cf923SMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13f04cf923SMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
14f04cf923SMarc-André Lureau  * furnished to do so, subject to the following conditions:
15f04cf923SMarc-André Lureau  *
16f04cf923SMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
17f04cf923SMarc-André Lureau  * all copies or substantial portions of the Software.
18f04cf923SMarc-André Lureau  *
19f04cf923SMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20f04cf923SMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21f04cf923SMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22f04cf923SMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23f04cf923SMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24f04cf923SMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25f04cf923SMarc-André Lureau  * THE SOFTWARE.
26f04cf923SMarc-André Lureau  */
27f04cf923SMarc-André Lureau 
28f04cf923SMarc-André Lureau #include "qemu/osdep.h"
29f04cf923SMarc-André Lureau 
30d3592199SMarc-André Lureau #include <glib/gprintf.h>
31d3592199SMarc-André Lureau 
32f04cf923SMarc-André Lureau #include "qemu/memfd.h"
33f04cf923SMarc-André Lureau 
3475e5b70eSPaolo Bonzini #if defined CONFIG_LINUX && !defined CONFIG_MEMFD
35f04cf923SMarc-André Lureau #include <sys/syscall.h>
36f04cf923SMarc-André Lureau #include <asm/unistd.h>
37f04cf923SMarc-André Lureau 
38d3592199SMarc-André Lureau static int memfd_create(const char *name, unsigned int flags)
39f04cf923SMarc-André Lureau {
40f04cf923SMarc-André Lureau #ifdef __NR_memfd_create
41f04cf923SMarc-André Lureau     return syscall(__NR_memfd_create, name, flags);
42f04cf923SMarc-André Lureau #else
43f04cf923SMarc-André Lureau     return -1;
44f04cf923SMarc-André Lureau #endif
45f04cf923SMarc-André Lureau }
46f04cf923SMarc-André Lureau #endif
47f04cf923SMarc-André Lureau 
48f04cf923SMarc-André Lureau #ifndef MFD_CLOEXEC
49f04cf923SMarc-André Lureau #define MFD_CLOEXEC 0x0001U
50f04cf923SMarc-André Lureau #endif
51f04cf923SMarc-André Lureau 
52f04cf923SMarc-André Lureau #ifndef MFD_ALLOW_SEALING
53f04cf923SMarc-André Lureau #define MFD_ALLOW_SEALING 0x0002U
54f04cf923SMarc-André Lureau #endif
55d3592199SMarc-André Lureau 
56*dcff1035SMarc-André Lureau int qemu_memfd_create(const char *name, size_t size, unsigned int seals)
57*dcff1035SMarc-André Lureau {
58*dcff1035SMarc-André Lureau     int mfd = -1;
59*dcff1035SMarc-André Lureau 
60*dcff1035SMarc-André Lureau #ifdef CONFIG_LINUX
61*dcff1035SMarc-André Lureau     unsigned int flags = MFD_CLOEXEC;
62*dcff1035SMarc-André Lureau 
63*dcff1035SMarc-André Lureau     if (seals) {
64*dcff1035SMarc-André Lureau         flags |= MFD_ALLOW_SEALING;
65*dcff1035SMarc-André Lureau     }
66*dcff1035SMarc-André Lureau 
67*dcff1035SMarc-André Lureau     mfd = memfd_create(name, flags);
68*dcff1035SMarc-André Lureau     if (mfd < 0) {
69*dcff1035SMarc-André Lureau         return -1;
70*dcff1035SMarc-André Lureau     }
71*dcff1035SMarc-André Lureau 
72*dcff1035SMarc-André Lureau     if (ftruncate(mfd, size) == -1) {
73*dcff1035SMarc-André Lureau         perror("ftruncate");
74*dcff1035SMarc-André Lureau         close(mfd);
75*dcff1035SMarc-André Lureau         return -1;
76*dcff1035SMarc-André Lureau     }
77*dcff1035SMarc-André Lureau 
78*dcff1035SMarc-André Lureau     if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
79*dcff1035SMarc-André Lureau         perror("fcntl");
80*dcff1035SMarc-André Lureau         close(mfd);
81*dcff1035SMarc-André Lureau         return -1;
82*dcff1035SMarc-André Lureau     }
83*dcff1035SMarc-André Lureau #endif
84*dcff1035SMarc-André Lureau 
85*dcff1035SMarc-André Lureau     return mfd;
86*dcff1035SMarc-André Lureau }
87*dcff1035SMarc-André Lureau 
88d3592199SMarc-André Lureau /*
89d3592199SMarc-André Lureau  * This is a best-effort helper for shared memory allocation, with
90d3592199SMarc-André Lureau  * optional sealing. The helper will do his best to allocate using
91d3592199SMarc-André Lureau  * memfd with sealing, but may fallback on other methods without
92d3592199SMarc-André Lureau  * sealing.
93d3592199SMarc-André Lureau  */
94d3592199SMarc-André Lureau void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
95d3592199SMarc-André Lureau                        int *fd)
96d3592199SMarc-André Lureau {
97d3592199SMarc-André Lureau     void *ptr;
98*dcff1035SMarc-André Lureau     int mfd = qemu_memfd_create(name, size, seals);
99d3592199SMarc-André Lureau 
100*dcff1035SMarc-André Lureau     /* some systems have memfd without sealing */
101*dcff1035SMarc-André Lureau     if (mfd == -1) {
102*dcff1035SMarc-André Lureau         mfd = qemu_memfd_create(name, size, 0);
103d3592199SMarc-André Lureau     }
104d3592199SMarc-André Lureau 
105d3592199SMarc-André Lureau     if (mfd == -1) {
10635f9b6efSMarc-André Lureau         const char *tmpdir = g_get_tmp_dir();
10735f9b6efSMarc-André Lureau         gchar *fname;
10835f9b6efSMarc-André Lureau 
10935f9b6efSMarc-André Lureau         fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
11035f9b6efSMarc-André Lureau         mfd = mkstemp(fname);
11135f9b6efSMarc-André Lureau         unlink(fname);
11235f9b6efSMarc-André Lureau         g_free(fname);
11335f9b6efSMarc-André Lureau 
11435f9b6efSMarc-André Lureau         if (mfd == -1) {
11535f9b6efSMarc-André Lureau             perror("mkstemp");
116d3592199SMarc-André Lureau             return NULL;
117d3592199SMarc-André Lureau         }
118d3592199SMarc-André Lureau 
11935f9b6efSMarc-André Lureau         if (ftruncate(mfd, size) == -1) {
12035f9b6efSMarc-André Lureau             perror("ftruncate");
12135f9b6efSMarc-André Lureau             close(mfd);
12235f9b6efSMarc-André Lureau             return NULL;
12335f9b6efSMarc-André Lureau         }
12435f9b6efSMarc-André Lureau     }
12535f9b6efSMarc-André Lureau 
126d3592199SMarc-André Lureau     ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
127d3592199SMarc-André Lureau     if (ptr == MAP_FAILED) {
128d3592199SMarc-André Lureau         perror("mmap");
129d3592199SMarc-André Lureau         close(mfd);
130d3592199SMarc-André Lureau         return NULL;
131d3592199SMarc-André Lureau     }
132d3592199SMarc-André Lureau 
133d3592199SMarc-André Lureau     *fd = mfd;
134d3592199SMarc-André Lureau     return ptr;
135d3592199SMarc-André Lureau }
136d3592199SMarc-André Lureau 
137d3592199SMarc-André Lureau void qemu_memfd_free(void *ptr, size_t size, int fd)
138d3592199SMarc-André Lureau {
139d3592199SMarc-André Lureau     if (ptr) {
140d3592199SMarc-André Lureau         munmap(ptr, size);
141d3592199SMarc-André Lureau     }
142d3592199SMarc-André Lureau 
143d3592199SMarc-André Lureau     if (fd != -1) {
144d3592199SMarc-André Lureau         close(fd);
145d3592199SMarc-André Lureau     }
146d3592199SMarc-André Lureau }
14731190ed7SMarc-André Lureau 
14831190ed7SMarc-André Lureau enum {
14931190ed7SMarc-André Lureau     MEMFD_KO,
15031190ed7SMarc-André Lureau     MEMFD_OK,
15131190ed7SMarc-André Lureau     MEMFD_TODO
15231190ed7SMarc-André Lureau };
15331190ed7SMarc-André Lureau 
15431190ed7SMarc-André Lureau bool qemu_memfd_check(void)
15531190ed7SMarc-André Lureau {
15631190ed7SMarc-André Lureau     static int memfd_check = MEMFD_TODO;
15731190ed7SMarc-André Lureau 
15831190ed7SMarc-André Lureau     if (memfd_check == MEMFD_TODO) {
15931190ed7SMarc-André Lureau         int fd;
16031190ed7SMarc-André Lureau         void *ptr;
16131190ed7SMarc-André Lureau 
16231190ed7SMarc-André Lureau         ptr = qemu_memfd_alloc("test", 4096, 0, &fd);
16331190ed7SMarc-André Lureau         memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
16431190ed7SMarc-André Lureau         qemu_memfd_free(ptr, 4096, fd);
16531190ed7SMarc-André Lureau     }
16631190ed7SMarc-André Lureau 
16731190ed7SMarc-André Lureau     return memfd_check == MEMFD_OK;
16831190ed7SMarc-André Lureau }
169