xref: /qemu/util/memfd.c (revision 0f2956f9)
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 
30*0f2956f9SMarc-André Lureau #include "qapi/error.h"
31f04cf923SMarc-André Lureau #include "qemu/memfd.h"
32f04cf923SMarc-André Lureau 
3375e5b70eSPaolo Bonzini #if defined CONFIG_LINUX && !defined CONFIG_MEMFD
34f04cf923SMarc-André Lureau #include <sys/syscall.h>
35f04cf923SMarc-André Lureau #include <asm/unistd.h>
36f04cf923SMarc-André Lureau 
37d3592199SMarc-André Lureau static int memfd_create(const char *name, unsigned int flags)
38f04cf923SMarc-André Lureau {
39f04cf923SMarc-André Lureau #ifdef __NR_memfd_create
40f04cf923SMarc-André Lureau     return syscall(__NR_memfd_create, name, flags);
41f04cf923SMarc-André Lureau #else
42f04cf923SMarc-André Lureau     return -1;
43f04cf923SMarc-André Lureau #endif
44f04cf923SMarc-André Lureau }
45f04cf923SMarc-André Lureau #endif
46f04cf923SMarc-André Lureau 
47f04cf923SMarc-André Lureau #ifndef MFD_CLOEXEC
48f04cf923SMarc-André Lureau #define MFD_CLOEXEC 0x0001U
49f04cf923SMarc-André Lureau #endif
50f04cf923SMarc-André Lureau 
51f04cf923SMarc-André Lureau #ifndef MFD_ALLOW_SEALING
52f04cf923SMarc-André Lureau #define MFD_ALLOW_SEALING 0x0002U
53f04cf923SMarc-André Lureau #endif
54d3592199SMarc-André Lureau 
55*0f2956f9SMarc-André Lureau int qemu_memfd_create(const char *name, size_t size,
56*0f2956f9SMarc-André Lureau                       unsigned int seals, Error **errp)
57dcff1035SMarc-André Lureau {
58dcff1035SMarc-André Lureau #ifdef CONFIG_LINUX
59*0f2956f9SMarc-André Lureau     int mfd = -1;
60dcff1035SMarc-André Lureau     unsigned int flags = MFD_CLOEXEC;
61dcff1035SMarc-André Lureau 
62dcff1035SMarc-André Lureau     if (seals) {
63dcff1035SMarc-André Lureau         flags |= MFD_ALLOW_SEALING;
64dcff1035SMarc-André Lureau     }
65dcff1035SMarc-André Lureau 
66dcff1035SMarc-André Lureau     mfd = memfd_create(name, flags);
67dcff1035SMarc-André Lureau     if (mfd < 0) {
68*0f2956f9SMarc-André Lureau         goto err;
69dcff1035SMarc-André Lureau     }
70dcff1035SMarc-André Lureau 
71dcff1035SMarc-André Lureau     if (ftruncate(mfd, size) == -1) {
72*0f2956f9SMarc-André Lureau         goto err;
73dcff1035SMarc-André Lureau     }
74dcff1035SMarc-André Lureau 
75dcff1035SMarc-André Lureau     if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
76*0f2956f9SMarc-André Lureau         goto err;
77dcff1035SMarc-André Lureau     }
78dcff1035SMarc-André Lureau 
79dcff1035SMarc-André Lureau     return mfd;
80*0f2956f9SMarc-André Lureau 
81*0f2956f9SMarc-André Lureau err:
82*0f2956f9SMarc-André Lureau     if (mfd >= 0) {
83*0f2956f9SMarc-André Lureau         close(mfd);
84*0f2956f9SMarc-André Lureau     }
85*0f2956f9SMarc-André Lureau #endif
86*0f2956f9SMarc-André Lureau     error_setg_errno(errp, errno, "failed to create memfd");
87*0f2956f9SMarc-André Lureau     return -1;
88dcff1035SMarc-André Lureau }
89dcff1035SMarc-André Lureau 
90d3592199SMarc-André Lureau /*
91d3592199SMarc-André Lureau  * This is a best-effort helper for shared memory allocation, with
92d3592199SMarc-André Lureau  * optional sealing. The helper will do his best to allocate using
93d3592199SMarc-André Lureau  * memfd with sealing, but may fallback on other methods without
94d3592199SMarc-André Lureau  * sealing.
95d3592199SMarc-André Lureau  */
96d3592199SMarc-André Lureau void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
97*0f2956f9SMarc-André Lureau                        int *fd, Error **errp)
98d3592199SMarc-André Lureau {
99d3592199SMarc-André Lureau     void *ptr;
100*0f2956f9SMarc-André Lureau     int mfd = qemu_memfd_create(name, size, seals, NULL);
101d3592199SMarc-André Lureau 
102dcff1035SMarc-André Lureau     /* some systems have memfd without sealing */
103dcff1035SMarc-André Lureau     if (mfd == -1) {
104*0f2956f9SMarc-André Lureau         mfd = qemu_memfd_create(name, size, 0, NULL);
105d3592199SMarc-André Lureau     }
106d3592199SMarc-André Lureau 
107d3592199SMarc-André Lureau     if (mfd == -1) {
10835f9b6efSMarc-André Lureau         const char *tmpdir = g_get_tmp_dir();
10935f9b6efSMarc-André Lureau         gchar *fname;
11035f9b6efSMarc-André Lureau 
11135f9b6efSMarc-André Lureau         fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
11235f9b6efSMarc-André Lureau         mfd = mkstemp(fname);
11335f9b6efSMarc-André Lureau         unlink(fname);
11435f9b6efSMarc-André Lureau         g_free(fname);
11535f9b6efSMarc-André Lureau 
116*0f2956f9SMarc-André Lureau         if (mfd == -1 ||
117*0f2956f9SMarc-André Lureau             ftruncate(mfd, size) == -1) {
118*0f2956f9SMarc-André Lureau             goto err;
11935f9b6efSMarc-André Lureau         }
12035f9b6efSMarc-André Lureau     }
12135f9b6efSMarc-André Lureau 
122d3592199SMarc-André Lureau     ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
123d3592199SMarc-André Lureau     if (ptr == MAP_FAILED) {
124*0f2956f9SMarc-André Lureau         goto err;
125d3592199SMarc-André Lureau     }
126d3592199SMarc-André Lureau 
127d3592199SMarc-André Lureau     *fd = mfd;
128d3592199SMarc-André Lureau     return ptr;
129*0f2956f9SMarc-André Lureau 
130*0f2956f9SMarc-André Lureau err:
131*0f2956f9SMarc-André Lureau     error_setg_errno(errp, errno, "failed to allocate shared memory");
132*0f2956f9SMarc-André Lureau     if (mfd >= 0) {
133*0f2956f9SMarc-André Lureau         close(mfd);
134*0f2956f9SMarc-André Lureau     }
135*0f2956f9SMarc-André Lureau     return NULL;
136d3592199SMarc-André Lureau }
137d3592199SMarc-André Lureau 
138d3592199SMarc-André Lureau void qemu_memfd_free(void *ptr, size_t size, int fd)
139d3592199SMarc-André Lureau {
140d3592199SMarc-André Lureau     if (ptr) {
141d3592199SMarc-André Lureau         munmap(ptr, size);
142d3592199SMarc-André Lureau     }
143d3592199SMarc-André Lureau 
144d3592199SMarc-André Lureau     if (fd != -1) {
145d3592199SMarc-André Lureau         close(fd);
146d3592199SMarc-André Lureau     }
147d3592199SMarc-André Lureau }
14831190ed7SMarc-André Lureau 
14931190ed7SMarc-André Lureau enum {
15031190ed7SMarc-André Lureau     MEMFD_KO,
15131190ed7SMarc-André Lureau     MEMFD_OK,
15231190ed7SMarc-André Lureau     MEMFD_TODO
15331190ed7SMarc-André Lureau };
15431190ed7SMarc-André Lureau 
15531190ed7SMarc-André Lureau bool qemu_memfd_check(void)
15631190ed7SMarc-André Lureau {
15731190ed7SMarc-André Lureau     static int memfd_check = MEMFD_TODO;
15831190ed7SMarc-André Lureau 
15931190ed7SMarc-André Lureau     if (memfd_check == MEMFD_TODO) {
16031190ed7SMarc-André Lureau         int fd;
16131190ed7SMarc-André Lureau         void *ptr;
16231190ed7SMarc-André Lureau 
163*0f2956f9SMarc-André Lureau         ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL);
16431190ed7SMarc-André Lureau         memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
16531190ed7SMarc-André Lureau         qemu_memfd_free(ptr, 4096, fd);
16631190ed7SMarc-André Lureau     }
16731190ed7SMarc-André Lureau 
16831190ed7SMarc-André Lureau     return memfd_check == MEMFD_OK;
16931190ed7SMarc-André Lureau }
170