From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39543) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bxqZQ-0006mi-7C for qemu-devel@nongnu.org; Sat, 22 Oct 2016 03:18:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bxqZM-0000jd-Kb for qemu-devel@nongnu.org; Sat, 22 Oct 2016 03:18:20 -0400 Received: from mail-lf0-x241.google.com ([2a00:1450:4010:c07::241]:36359) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1bxqZL-0000jR-Uj for qemu-devel@nongnu.org; Sat, 22 Oct 2016 03:18:16 -0400 Received: by mail-lf0-x241.google.com with SMTP id b75so7356050lfg.3 for ; Sat, 22 Oct 2016 00:18:15 -0700 (PDT) MIME-Version: 1.0 References: <20161022070041.23763-1-rafael.tinoco@canonical.com> In-Reply-To: <20161022070041.23763-1-rafael.tinoco@canonical.com> From: =?UTF-8?B?TWFyYy1BbmRyw6kgTHVyZWF1?= Date: Sat, 22 Oct 2016 07:18:02 +0000 Message-ID: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH] vhost: secure vhost shared log files using argv paremeter List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Rafael David Tinoco Cc: QEMU Hi On Sat, Oct 22, 2016 at 10:01 AM Rafael David Tinoco < rafael.tinoco@canonical.com> wrote: Commit 31190ed7 added a migration blocker in vhost_dev_init() to check if memfd would succeed. It is better if this blocker first checks if vhost backend requires shared log. This will avoid a situation where a blocker is added inappropriately (e.g. shared log allocation fails when vhost backend doesn't support it). Could you make this a seperate patch? Commit: 35f9b6e added a fallback mechanism for systems not supporting memfd_create syscall (started being supported since 3.17). Backporting memfd_create might not be accepted for distros relying on older kernels. Nowadays there is no way for security driver to discover memfd filename to be created: /memfd-XXXXXX. Also, because vhost log file descriptors can be passed to other processes, after discussion, we thought it is best to back mmap by using files that can be placed into a specific directory: this commit creates "vhostlog" argv parameter for such purpose. This will allow security drivers to operate on those files appropriately. Argv examples: -netdev tap,id=3Dnet0,vhost=3Don -netdev tap,id=3Dnet0,vhost=3Don,vhostlog=3D/tmp/guest.log -netdev tap,id=3Dnet0,vhost=3Don,vhostlog=3D/tmp Could it be only a filename? This would simplify testing. For vhost backends supporting shared logs, if vhostlog is non-existent, or a directory, random files are going to be created in the specified directory (or, for non-existent, in tmpdir). If vhostlog is specified, the filepath is always used when allocating vhost log files. Regarding testing, you add utility code mmap-file, could you make this a seperate commit, with unit tests? thanks Signed-off-by: Rafael David Tinoco --- hw/net/vhost_net.c | 4 +- hw/scsi/vhost-scsi.c | 2 +- hw/virtio/vhost-vsock.c | 2 +- hw/virtio/vhost.c | 41 +++++++------ include/hw/virtio/vhost.h | 4 +- include/net/vhost_net.h | 1 + include/qemu/mmap-file.h | 10 +++ net/tap.c | 6 ++ qapi-schema.json | 3 + qemu-options.hx | 3 +- util/Makefile.objs | 1 + util/mmap-file.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 207 insertions(+), 23 deletions(-) create mode 100644 include/qemu/mmap-file.h create mode 100644 util/mmap-file.c diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index f2d49ad..d650c92 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -171,8 +171,8 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.vq_index =3D net->nc->queue_index * net->dev.nvqs; } - r =3D vhost_dev_init(&net->dev, options->opaque, - options->backend_type, options->busyloop_timeout); + r =3D vhost_dev_init(&net->dev, options->opaque, options->backend_type= , + options->busyloop_timeout, options->vhostlog); if (r < 0) { goto fail; } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 5b26946..5dc3d30 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -248,7 +248,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) s->dev.backend_features =3D 0; ret =3D vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL, 0); + VHOST_BACKEND_TYPE_KERNEL, 0, NULL); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index b481562..6cf6081 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -342,7 +342,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) vsock->vhost_dev.nvqs =3D ARRAY_SIZE(vsock->vhost_vqs); vsock->vhost_dev.vqs =3D vsock->vhost_vqs; ret =3D vhost_dev_init(&vsock->vhost_dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL, 0); + VHOST_BACKEND_TYPE_KERNEL, 0, NULL); if (ret < 0) { error_setg_errno(errp, -ret, "vhost-vsock: vhost_dev_init failed")= ; goto err_virtio; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index bd051ab..d874ebb 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -20,7 +20,7 @@ #include "qemu/atomic.h" #include "qemu/range.h" #include "qemu/error-report.h" -#include "qemu/memfd.h" +#include "qemu/mmap-file.h" #include #include "exec/address-spaces.h" #include "hw/virtio/virtio-bus.h" @@ -326,7 +326,7 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev) return log_size; } -static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) +static struct vhost_log *vhost_log_alloc(char *path, uint64_t size, bool share) { struct vhost_log *log; uint64_t logsize =3D size * sizeof(*(log->log)); @@ -334,9 +334,7 @@ static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) log =3D g_new0(struct vhost_log, 1); if (share) { - log->log =3D qemu_memfd_alloc("vhost-log", logsize, - F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, - &fd); + log->log =3D qemu_mmap_alloc(path, logsize, &fd); memset(log->log, 0, logsize); } else { log->log =3D g_malloc0(logsize); @@ -349,12 +347,12 @@ static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) return log; } -static struct vhost_log *vhost_log_get(uint64_t size, bool share) +static struct vhost_log *vhost_log_get(char *path, uint64_t size, bool share) { struct vhost_log *log =3D share ? vhost_log_shm : vhost_log; if (!log || log->size !=3D size) { - log =3D vhost_log_alloc(size, share); + log =3D vhost_log_alloc(path, size, share); if (share) { vhost_log_shm =3D log; } else { @@ -388,8 +386,7 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) g_free(log->log); vhost_log =3D NULL; } else if (vhost_log_shm =3D=3D log) { - qemu_memfd_free(log->log, log->size * sizeof(*(log->log)), - log->fd); + qemu_mmap_free(log->log, log->size * sizeof(*(log->log)), log->fd); vhost_log_shm =3D NULL; } @@ -405,9 +402,12 @@ static bool vhost_dev_log_is_shared(struct vhost_dev *dev) static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) { - struct vhost_log *log =3D vhost_log_get(size, vhost_dev_log_is_shared(dev)); - uint64_t log_base =3D (uintptr_t)log->log; int r; + struct vhost_log *log; + uint64_t log_base; + + log =3D vhost_log_get(dev->log_filename, size, vhost_dev_log_is_shared(dev)); + log_base =3D (uintptr_t)log->log; /* inform backend of log switching, this must be done before releasing the current log, to ensure no logging is lost */ @@ -1049,7 +1049,8 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type, uint32_t busyloop_timeout) + VhostBackendType backend_type, + uint32_t busyloop_timeout, char *vhostlog) { uint64_t features; int i, r, n_initialized_vqs =3D 0; @@ -1118,11 +1119,18 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .priority =3D 10 }; + hdev->log =3D NULL; + hdev->log_size =3D 0; + hdev->log_enabled =3D false; + hdev->log_filename =3D vhostlog ? g_strdup(vhostlog) : NULL; + g_free(vhostlog); + if (hdev->migration_blocker =3D=3D NULL) { if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { error_setg(&hdev->migration_blocker, "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature."); - } else if (!qemu_memfd_check()) { + } else if (vhost_dev_log_is_shared(hdev) && + !qemu_mmap_check(hdev->log_filename)) { error_setg(&hdev->migration_blocker, "Migration disabled: failed to allocate shared memory"); } @@ -1135,9 +1143,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, hdev->mem =3D g_malloc0(offsetof(struct vhost_memory, regions)); hdev->n_mem_sections =3D 0; hdev->mem_sections =3D NULL; - hdev->log =3D NULL; - hdev->log_size =3D 0; - hdev->log_enabled =3D false; hdev->started =3D false; hdev->memory_changed =3D false; memory_listener_register(&hdev->memory_listener, &address_space_memory); @@ -1175,6 +1180,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) if (hdev->vhost_ops) { hdev->vhost_ops->vhost_backend_cleanup(hdev); } + g_free(hdev->log_filename); assert(!hdev->log); memset(hdev, 0, sizeof(struct vhost_dev)); @@ -1335,7 +1341,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) uint64_t log_base; hdev->log_size =3D vhost_get_log_size(hdev); - hdev->log =3D vhost_log_get(hdev->log_size, + hdev->log =3D vhost_log_get(hdev->log_filename, + hdev->log_size, vhost_dev_log_is_shared(hdev)); log_base =3D (uintptr_t)hdev->log->log; r =3D hdev->vhost_ops->vhost_set_log_base(hdev, diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index e433089..1ea4f3a 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -52,6 +52,7 @@ struct vhost_dev { uint64_t max_queues; bool started; bool log_enabled; + char *log_filename; uint64_t log_size; Error *migration_blocker; bool memory_changed; @@ -65,7 +66,8 @@ struct vhost_dev { int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, - uint32_t busyloop_timeout); + uint32_t busyloop_timeout, + char *vhostlog); void vhost_dev_cleanup(struct vhost_dev *hdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 5a08eff..94161b2 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -12,6 +12,7 @@ typedef struct VhostNetOptions { NetClientState *net_backend; uint32_t busyloop_timeout; void *opaque; + char *vhostlog; } VhostNetOptions; uint64_t vhost_net_get_max_queues(VHostNetState *net); diff --git a/include/qemu/mmap-file.h b/include/qemu/mmap-file.h new file mode 100644 index 0000000..427612a --- /dev/null +++ b/include/qemu/mmap-file.h @@ -0,0 +1,10 @@ +#ifndef QEMU_MMAP_FILE_H +#define QEMU_MMAP_FILE_H + +#include "qemu-common.h" + +void *qemu_mmap_alloc(const char *path, size_t size, int *fd); +void qemu_mmap_free(void *ptr, size_t size, int fd); +bool qemu_mmap_check(const char *path); + +#endif diff --git a/net/tap.c b/net/tap.c index b6896a7..7b242cd 100644 --- a/net/tap.c +++ b/net/tap.c @@ -699,6 +699,12 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } options.opaque =3D (void *)(uintptr_t)vhostfd; + if (tap->has_vhostlog) { + options.vhostlog =3D g_strdup(tap->vhostlog); + } else { + options.vhostlog =3D NULL; + } + s->vhost_net =3D vhost_net_init(&options); if (!s->vhost_net) { error_setg(errp, diff --git a/qapi-schema.json b/qapi-schema.json index 5a8ec38..72608bd 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2640,6 +2640,8 @@ # # @vhostforce: #optional vhost on for non-MSIX virtio guests # +# @vhostlog: #optional file or directory for vhost backend log +# # @queues: #optional number of queues to be created for multiqueue capable tap # # @poll-us: #optional maximum number of microseconds that could @@ -2662,6 +2664,7 @@ '*vhostfd': 'str', '*vhostfds': 'str', '*vhostforce': 'bool', + '*vhostlog': 'str', '*queues': 'uint32', '*poll-us': 'uint32'} } diff --git a/qemu-options.hx b/qemu-options.hx index b1fbdb0..5c09c09 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1599,7 +1599,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #else "-netdev tap,id=3Dstr[,fd=3Dh][,fds=3Dx:y:...:z][,ifname=3Dname][,script=3Dfile][,do= wnscript=3Ddfile]\n" " [,br=3Dbridge][,helper=3Dhelper][,sndbuf=3Dnbytes][,vnet_hdr=3Don|off][,vh= ost=3Don|off]\n" - " [,vhostfd=3Dh][,vhostfds=3Dx:y:...:z][,vhostforce=3Don|off][,queues=3Dn]\n= " + " [,vhostfd=3Dh][,vhostfds=3Dx:y:...:z][,vhostforce=3Don|off][,vhostlog=3Dfi= le|dir][,queues=3Dn]\n" " [,poll-us=3Dn]\n" " configure a host TAP network backend with ID 'str'\n" " connected to a bridge (default=3D" DEFAULT_BRIDGE_INTERFACE ")\n" @@ -1618,6 +1618,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " use vhost=3Don to enable experimental in kernel accelerator\n" " (only has effect for virtio guests which use MSIX)\n" " use vhostforce=3Don to force vhost on for non-MSIX virtio guests\n" + " use 'vhostlog=3Dfile|dir' file or directory for vhost backend log\n" " use 'vhostfd=3Dh' to connect to an already opened vho= st net device\n" " use 'vhostfds=3Dx:y:...:z to connect to multiple already opened vhost net devices\n" " use 'queues=3Dn' to specify the number of queues to b= e created for multiqueue TAP\n" diff --git a/util/Makefile.objs b/util/Makefile.objs index 36c7dcc..69bb27a 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -3,6 +3,7 @@ util-obj-y +=3D bufferiszero.o util-obj-$(CONFIG_POSIX) +=3D compatfd.o util-obj-$(CONFIG_POSIX) +=3D event_notifier-posix.o util-obj-$(CONFIG_POSIX) +=3D mmap-alloc.o +util-obj-$(CONFIG_POSIX) +=3D mmap-file.o util-obj-$(CONFIG_POSIX) +=3D oslib-posix.o util-obj-$(CONFIG_POSIX) +=3D qemu-openpty.o util-obj-$(CONFIG_POSIX) +=3D qemu-thread-posix.o diff --git a/util/mmap-file.c b/util/mmap-file.c new file mode 100644 index 0000000..ce778cf --- /dev/null +++ b/util/mmap-file.c @@ -0,0 +1,153 @@ +/* + * Support for file backed by mmaped host memory. + * + * Authors: + * Rafael David Tinoco + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/mmap-file.h" + +static char *qemu_mmap_rand_name(void) +{ + char *name; + GRand *rsufix; + guint32 sufix; + + rsufix =3D g_rand_new(); + sufix =3D g_rand_int(rsufix); + g_free(rsufix); + name =3D g_strdup_printf("mmap-%u", sufix); + + return name; +} + +static inline void qemu_mmap_rand_name_free(char *str) +{ + g_free(str); +} + +static bool qemu_mmap_is(const char *path, mode_t what) +{ + struct stat s; + + memset(&s, 0, sizeof(struct stat)); + if (stat(path, &s)) { + perror("stat"); + goto negative; + } + + if ((s.st_mode & S_IFMT) =3D=3D what) { + return true; + } + +negative: + return false; +} + +static inline bool qemu_mmap_is_file(const char *path) +{ + return qemu_mmap_is(path, S_IFREG); +} + +static inline bool qemu_mmap_is_dir(const char *path) +{ + return qemu_mmap_is(path, S_IFDIR); +} + +static void *qemu_mmap_alloc_file(const char *filepath, size_t size, int *fd) +{ + void *ptr; + int mfd =3D -1; + + *fd =3D -1; + + mfd =3D open(filepath, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); + if (mfd =3D=3D -1) { + perror("open"); + return NULL; + } + + unlink(filepath); + + if (ftruncate(mfd, size) =3D=3D -1) { + perror("ftruncate"); + close(mfd); + return NULL; + } + + ptr =3D mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0); + if (ptr =3D=3D MAP_FAILED) { + perror("mmap"); + close(mfd); + return NULL; + } + + *fd =3D mfd; + return ptr; +} + +static void *qemu_mmap_alloc_dir(const char *dirpath, size_t size, int *fd= ) +{ + void *ptr; + char *file, *rand, *tmp, *dir2use =3D NULL; + + if (dirpath && !qemu_mmap_is_dir(dirpath)) { + return NULL; + } + + tmp =3D (char *) g_get_tmp_dir(); + dir2use =3D dirpath ? (char *) dirpath : tmp; + rand =3D qemu_mmap_rand_name(); + file =3D g_strdup_printf("%s/%s", dir2use, rand); + ptr =3D qemu_mmap_alloc_file(file, size, fd); + g_free(tmp); + qemu_mmap_rand_name_free(rand); + + return ptr; +} + +/* + * "path" can be: + * + * filename =3D full path for the file to back mmap + * dir path =3D full dir path where to create random file for mmap + * null =3D will use to create random file for mmap + */ +void *qemu_mmap_alloc(const char *path, size_t size, int *fd) +{ + if (!path || qemu_mmap_is_dir(path)) { + return qemu_mmap_alloc_dir(path, size, fd); + } + + return qemu_mmap_alloc_file(path, size, fd); +} + +void qemu_mmap_free(void *ptr, size_t size, int fd) +{ + if (ptr) { + munmap(ptr, size); + } + + if (fd !=3D -1) { + close(fd); + } +} + +bool qemu_mmap_check(const char *path) +{ + void *ptr; + int fd =3D -1; + bool r =3D true; + + ptr =3D qemu_mmap_alloc(path, 4096, &fd); + if (!ptr) { + r =3D false; + } + qemu_mmap_free(ptr, 4096, fd); + + return r =3D=3D true ? true : false; +} -- 2.9.3 --=20 Marc-Andr=C3=A9 Lureau