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