[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#856591: marked as done (unblock: qemu/1:2.8+dfsg-3)



Your message dated Thu, 2 Mar 2017 23:48:41 +0100
with message-id <20170302224838.GA9403@ugent.be>
and subject line Re: unblock: qemu/1:2.8+dfsg-3
has caused the Debian Bug report #856591,
regarding unblock: qemu/1:2.8+dfsg-3
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
856591: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=856591
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package qemu

There's a large pile of security bugs fixed in this release,
all fixes comes from upstream.

Plus a few other non-security bugs, some more serious, some
less, but all of them are annoying enough and does not allow
usage of qemu in certain environments or for certain tasks.
Again, everything comes from upstream.  Not every bugfix
has corresponding bug in Debian BTS.

Plus a few very small fixes here and there - for example,
ubuntu changes which are _not_, in any way, change debian
packaging, only ubuntu part of the control-in file.  This
is in order for us to easily work together during stretch
life cycle.  I especially picked only the changes which
are not changing Debian parts, to simplify review/unblock.

The bulk of the changes (the debdiff is quite large) comes
from one patch, which is a fix for CVE-2016-9602, #853006,
which is a 9pfs symlink attack issue. The patch is a
combination of 28 patches from upstream fold into one file,
with the diffstat:

 hw/9pfs/9p-local.c      | 1023 ++++++++++++++++++++++++++---------------------
 hw/9pfs/9p-local.h      |   20 +
 hw/9pfs/9p-posix-acl.c  |   44 +-
 hw/9pfs/9p-util.c       |   69 ++++
 hw/9pfs/9p-util.h       |   54 +++
 hw/9pfs/9p-xattr-user.c |   24 +-
 hw/9pfs/9p-xattr.c      |  166 +++++++-
 hw/9pfs/9p-xattr.h      |   87 +---
 hw/9pfs/Makefile.objs   |    2 +-
 9 files changed, 893 insertions(+), 596 deletions(-)
 create mode 100644 hw/9pfs/9p-local.h
 create mode 100644 hw/9pfs/9p-util.c
 create mode 100644 hw/9pfs/9p-util.h

This is because the _architecture_ of 9pfs needed to be
changed in order to fix the issue.  There are other issues
similar to this one pending in 9pfs, but that's it for now.

I'm sorry for this large changeset this late in the
release cycle, but here we go - it is 99.9% bugs...
I can't do anything with these except patching :)

Thanks,

/mjt

unblock qemu/1:2.8+dfsg-3

diff -Nru qemu-2.8+dfsg/debian/changelog qemu-2.8+dfsg/debian/changelog
--- qemu-2.8+dfsg/debian/changelog	2017-01-23 14:06:54.000000000 +0300
+++ qemu-2.8+dfsg/debian/changelog	2017-03-01 12:32:26.000000000 +0300
@@ -1,3 +1,63 @@
+qemu (1:2.8+dfsg-3) unstable; urgency=high
+
+  * urgency high due to security fixes
+
+  [ Michael Tokarev ]
+  * serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch
+    Closes: #853002, CVE-2017-5579
+  * cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch
+    (needed for the next patch, CVE-2017-2620 fix)
+  * cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch
+    Closes: #855791, CVE-2017-2620
+  * nbd_client-fix-drop_sync-CVE-2017-2630.diff
+    Closes: #855227, CVE-2017-2630
+  * sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch
+    Closes: #855159, CVE-2017-5987
+  * vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch
+    Closes: #855616, CVE-2017-6058
+  * 3 CVE fixes from upstream for #853996:
+    sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch
+    megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch
+    virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch
+    Closes: #853996, CVE-2017-5667, CVE-2017-5856, CVE-2017-5857
+  * usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch
+    Closes: #854729, CVE-2017-5898
+  * virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch
+    Closes: #854730, CVE-2017-5931
+  * xhci-apply-limits-to-loops-CVE-2017-5973.patch
+    Closes: #855611, CVE-2017-5973
+  * net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch
+    Closes: #839986, CVE-2016-7907
+  * cirrus-fix-oob-access-issue-CVE-2017-2615.patch
+    Closes: #854731, CVE-2017-2615
+  * 9pfs-symlink-attack-fixes-CVE-2016-9602.patch
+    Closes: #853006
+  * vnc-do-not-disconnect-on-EAGAIN.patch
+    Closes: #854032
+  * xhci-fix-event-queue-IRQ-handling.patch (win7 xhci issue fix)
+  * xhci-only-free-completed-transfers.patch
+    Closes: #855659
+  * char-fix-ctrl-a-b-not-working.patch
+    Closes: https://bugs.launchpad.net/bugs/1654137
+  * char-drop-data-written-to-a-disconnected-pty.patch
+    Closes: https://bugs.launchpad.net/bugs/1667033
+  * s390x-use-qemu-cpu-model-in-user-mode.patch
+    Closes: #854893
+  * d/control is autogenerated, add comment
+  * check if debootstrap is available in qemu-debootstrap
+    Closes: #846497
+
+  [ Christian Ehrhardt ]
+  * (ubuntu) no more skip enable libiscsi (now in main)
+  * (ubuntu) Disable glusterfs (Universe dependency)
+  * (ubuntu) have qemu-system-arm suggest: qemu-efi;
+    this should be a stronger relationship, but qemu-efi is still
+    in universe right now.
+  * (ubuntu) change dependencies for fix of wrong acl for newly
+    created device node on ubuntu
+
+ -- Michael Tokarev <mjt@tls.msk.ru>  Tue, 28 Feb 2017 11:40:18 +0300
+
 qemu (1:2.8+dfsg-2) unstable; urgency=medium
 
   * Revert "update binfmt registration for mipsn32"
diff -Nru qemu-2.8+dfsg/debian/control qemu-2.8+dfsg/debian/control
--- qemu-2.8+dfsg/debian/control	2017-01-23 13:34:27.000000000 +0300
+++ qemu-2.8+dfsg/debian/control	2017-03-01 12:09:21.000000000 +0300
@@ -1,3 +1,4 @@
+# autogenerated file, please edit debian/control-in
 Source: qemu
 Section: otherosfs
 Priority: optional
@@ -62,6 +63,7 @@
  libpulse-dev,
 # --enable-rbd		linux-*
  librados-dev [linux-any], librbd-dev [linux-any],
+# glusterfs is debian-only since ubuntu/glusterfs is in universe (MIR LP: #1274247)
 # --enable-glusterfs
  glusterfs-common,
 # --enable-vnc-sasl
diff -Nru qemu-2.8+dfsg/debian/control-in qemu-2.8+dfsg/debian/control-in
--- qemu-2.8+dfsg/debian/control-in	2017-01-23 13:04:57.000000000 +0300
+++ qemu-2.8+dfsg/debian/control-in	2017-03-01 11:24:40.000000000 +0300
@@ -48,8 +48,8 @@
 # vte is used together with gtk
 # --disable-vte
 # libiscsi is debian-only since ubuntu/libiscsi is in universe
-:debian:# --enable-libiscsi
-:debian: libiscsi-dev (>> 1.9.0~),
+# --enable-libiscsi
+ libiscsi-dev (>> 1.9.0~),
 # --enable-curses
  libncursesw5-dev,
 :debian:# --enable-libnfs
@@ -64,8 +64,9 @@
  libpulse-dev,
 # --enable-rbd		linux-*
  librados-dev [linux-any], librbd-dev [linux-any],
-# --enable-glusterfs
- glusterfs-common,
+# glusterfs is debian-only since ubuntu/glusterfs is in universe (MIR LP: #1274247)
+:debian:# --enable-glusterfs
+:debian: glusterfs-common,
 # --enable-vnc-sasl
  libsasl2-dev,
 # note: libsdl2-dev is in universe on ubuntu
@@ -187,7 +188,7 @@
 Provides: qemu-keymaps
 Depends: ${misc:Depends}, ${shlibs:Depends},
 # to fix wrong acl for newly created device node on ubuntu:
-:ubuntu: udev, acl
+:ubuntu: acl
 Breaks:
 :ubuntu: qemu-common,
 # qemu-system before qemu-system-split
@@ -248,8 +249,9 @@
 Recommends: qemu-utils,
 # aarch64 arm uses bootroms
  ipxe-qemu (>= 1.0.0+git-20131111.c3d1e78-1~),
- qemu-efi
+:debian: qemu-efi
 Suggests: samba, vde2, qemu-block-extra (= ${binary:Version}),
+:ubuntu: qemu-efi
 Provides: ${sysprovides:arm}
 Breaks: qemu-system (<< 1.3.0+dfsg-5),
 :ubuntu: qemu-common (<< 1.3.0+dfsg-5),
diff -Nru qemu-2.8+dfsg/debian/patches/9pfs-symlink-attack-fixes-CVE-2016-9602.patch qemu-2.8+dfsg/debian/patches/9pfs-symlink-attack-fixes-CVE-2016-9602.patch
--- qemu-2.8+dfsg/debian/patches/9pfs-symlink-attack-fixes-CVE-2016-9602.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/9pfs-symlink-attack-fixes-CVE-2016-9602.patch	2017-03-01 12:08:12.000000000 +0300
@@ -0,0 +1,2185 @@
+From: Greg Kurz <groug@kaod.org>
+Subject: patchset addressing 9pfs symlink attack vulnerability (CVE-2016-9602)
+Bug-Debian: http://bugs.debian.org/853006
+
+Consists of the following commits:
+
+Greg Kurz (28):
+      9pfs: local: move xattr security ops to 9p-xattr.c
+      9pfs: remove side-effects in local_init()
+      9pfs: remove side-effects in local_open() and local_opendir()
+      9pfs: introduce relative_openat_nofollow() helper
+      9pfs: local: keep a file descriptor on the shared folder
+      9pfs: local: open/opendir: don't follow symlinks
+      9pfs: local: lgetxattr: don't follow symlinks
+      9pfs: local: llistxattr: don't follow symlinks
+      9pfs: local: lsetxattr: don't follow symlinks
+      9pfs: local: lremovexattr: don't follow symlinks
+      9pfs: local: unlinkat: don't follow symlinks
+      9pfs: local: remove: don't follow symlinks
+      9pfs: local: utimensat: don't follow symlinks
+      9pfs: local: statfs: don't follow symlinks
+      9pfs: local: truncate: don't follow symlinks
+      9pfs: local: readlink: don't follow symlinks
+      9pfs: local: lstat: don't follow symlinks
+      9pfs: local: renameat: don't follow symlinks
+      9pfs: local: rename: use renameat
+      9pfs: local: improve error handling in link op
+      9pfs: local: link: don't follow symlinks
+      9pfs: local: chmod: don't follow symlinks
+      9pfs: local: chown: don't follow symlinks
+      9pfs: local: symlink: don't follow symlinks
+      9pfs: local: mknod: don't follow symlinks
+      9pfs: local: mkdir: don't follow symlinks
+      9pfs: local: open2: don't follow symlinks
+      9pfs: local: drop unused code
+
+ hw/9pfs/9p-local.c      | 1023 ++++++++++++++++++++++++++---------------------
+ hw/9pfs/9p-local.h      |   20 +
+ hw/9pfs/9p-posix-acl.c  |   44 +-
+ hw/9pfs/9p-util.c       |   69 ++++
+ hw/9pfs/9p-util.h       |   54 +++
+ hw/9pfs/9p-xattr-user.c |   24 +-
+ hw/9pfs/9p-xattr.c      |  166 +++++++-
+ hw/9pfs/9p-xattr.h      |   87 +---
+ hw/9pfs/Makefile.objs   |    2 +-
+ 9 files changed, 893 insertions(+), 596 deletions(-)
+ create mode 100644 hw/9pfs/9p-local.h
+ create mode 100644 hw/9pfs/9p-util.c
+ create mode 100644 hw/9pfs/9p-util.h
+
+diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
+index 7de07e1..432a30c 100644
+--- a/hw/9pfs/9p-local.c
++++ b/hw/9pfs/9p-local.c
+@@ -13,7 +13,9 @@
+ 
+ #include "qemu/osdep.h"
+ #include "9p.h"
++#include "9p-local.h"
+ #include "9p-xattr.h"
++#include "9p-util.h"
+ #include "fsdev/qemu-fsdev.h"   /* local_ops */
+ #include <arpa/inet.h>
+ #include <pwd.h>
+@@ -43,40 +45,62 @@
+ #define BTRFS_SUPER_MAGIC 0x9123683E
+ #endif
+ 
+-#define VIRTFS_META_DIR ".virtfs_metadata"
++typedef struct {
++    int mountfd;
++} LocalData;
+ 
+-static char *local_mapped_attr_path(FsContext *ctx, const char *path)
++int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
++                        mode_t mode)
+ {
+-    int dirlen;
+-    const char *name = strrchr(path, '/');
+-    if (name) {
+-        dirlen = name - path;
+-        ++name;
+-    } else {
+-        name = path;
+-        dirlen = 0;
++    LocalData *data = fs_ctx->private;
++
++    /* All paths are relative to the path data->mountfd points to */
++    while (*path == '/') {
++        path++;
+     }
+-    return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
+-                           dirlen, path, VIRTFS_META_DIR, name);
++
++    return relative_openat_nofollow(data->mountfd, path, flags, mode);
++}
++
++int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
++{
++    return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
++}
++
++static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
++                                    const char *npath)
++{
++    int serrno = errno;
++    renameat(odirfd, opath, ndirfd, npath);
++    errno = serrno;
+ }
+ 
+-static FILE *local_fopen(const char *path, const char *mode)
++static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
++{
++    int serrno = errno;
++    unlinkat(dirfd, path, flags);
++    errno = serrno;
++}
++
++#define VIRTFS_META_DIR ".virtfs_metadata"
++
++static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
+ {
+     int fd, o_mode = 0;
+     FILE *fp;
+-    int flags = O_NOFOLLOW;
++    int flags;
+     /*
+      * only supports two modes
+      */
+     if (mode[0] == 'r') {
+-        flags |= O_RDONLY;
++        flags = O_RDONLY;
+     } else if (mode[0] == 'w') {
+-        flags |= O_WRONLY | O_TRUNC | O_CREAT;
++        flags = O_WRONLY | O_TRUNC | O_CREAT;
+         o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+     } else {
+         return NULL;
+     }
+-    fd = open(path, flags, o_mode);
++    fd = openat_file(dirfd, name, flags, o_mode);
+     if (fd == -1) {
+         return NULL;
+     }
+@@ -88,16 +112,20 @@ static FILE *local_fopen(const char *path, const char *mode)
+ }
+ 
+ #define ATTR_MAX 100
+-static void local_mapped_file_attr(FsContext *ctx, const char *path,
++static void local_mapped_file_attr(int dirfd, const char *name,
+                                    struct stat *stbuf)
+ {
+     FILE *fp;
+     char buf[ATTR_MAX];
+-    char *attr_path;
++    int map_dirfd;
+ 
+-    attr_path = local_mapped_attr_path(ctx, path);
+-    fp = local_fopen(attr_path, "r");
+-    g_free(attr_path);
++    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++    if (map_dirfd == -1) {
++        return;
++    }
++
++    fp = local_fopenat(map_dirfd, name, "r");
++    close_preserve_errno(map_dirfd);
+     if (!fp) {
+         return;
+     }
+@@ -119,12 +147,17 @@ static void local_mapped_file_attr(FsContext *ctx, const char *path,
+ 
+ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
+ {
+-    int err;
+-    char *buffer;
+-    char *path = fs_path->data;
++    int err = -1;
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
+ 
+-    buffer = rpath(fs_ctx, path);
+-    err =  lstat(buffer, stbuf);
++    err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
+     if (err) {
+         goto err_out;
+     }
+@@ -134,87 +167,83 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
+         gid_t tmp_gid;
+         mode_t tmp_mode;
+         dev_t tmp_dev;
+-        if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
++
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
++                                 sizeof(uid_t)) > 0) {
+             stbuf->st_uid = le32_to_cpu(tmp_uid);
+         }
+-        if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
++                                 sizeof(gid_t)) > 0) {
+             stbuf->st_gid = le32_to_cpu(tmp_gid);
+         }
+-        if (getxattr(buffer, "user.virtfs.mode",
+-                    &tmp_mode, sizeof(mode_t)) > 0) {
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
++                                 sizeof(mode_t)) > 0) {
+             stbuf->st_mode = le32_to_cpu(tmp_mode);
+         }
+-        if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
++                                 sizeof(dev_t)) > 0) {
+             stbuf->st_rdev = le64_to_cpu(tmp_dev);
+         }
+     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        local_mapped_file_attr(fs_ctx, path, stbuf);
++        local_mapped_file_attr(dirfd, name, stbuf);
+     }
+ 
+ err_out:
+-    g_free(buffer);
+-    return err;
+-}
+-
+-static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
+-{
+-    int err;
+-    char *attr_dir;
+-    char *tmp_path = g_strdup(path);
+-
+-    attr_dir = g_strdup_printf("%s/%s/%s",
+-             ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
+-
+-    err = mkdir(attr_dir, 0700);
+-    if (err < 0 && errno == EEXIST) {
+-        err = 0;
+-    }
+-    g_free(attr_dir);
+-    g_free(tmp_path);
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
+     return err;
+ }
+ 
+-static int local_set_mapped_file_attr(FsContext *ctx,
+-                                      const char *path, FsCred *credp)
++static int local_set_mapped_file_attrat(int dirfd, const char *name,
++                                        FsCred *credp)
+ {
+     FILE *fp;
+-    int ret = 0;
++    int ret;
+     char buf[ATTR_MAX];
+-    char *attr_path;
+     int uid = -1, gid = -1, mode = -1, rdev = -1;
++    int map_dirfd;
++
++    ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
++    if (ret < 0 && errno != EEXIST) {
++        return -1;
++    }
+ 
+-    attr_path = local_mapped_attr_path(ctx, path);
+-    fp = local_fopen(attr_path, "r");
++    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++    if (map_dirfd == -1) {
++        return -1;
++    }
++
++    fp = local_fopenat(map_dirfd, name, "r");
+     if (!fp) {
+-        goto create_map_file;
++        if (errno == ENOENT) {
++            goto update_map_file;
++        } else {
++            close_preserve_errno(map_dirfd);
++            return -1;
++        }
+     }
+     memset(buf, 0, ATTR_MAX);
+     while (fgets(buf, ATTR_MAX, fp)) {
+         if (!strncmp(buf, "virtfs.uid", 10)) {
+-            uid = atoi(buf+11);
++            uid = atoi(buf + 11);
+         } else if (!strncmp(buf, "virtfs.gid", 10)) {
+-            gid = atoi(buf+11);
++            gid = atoi(buf + 11);
+         } else if (!strncmp(buf, "virtfs.mode", 11)) {
+-            mode = atoi(buf+12);
++            mode = atoi(buf + 12);
+         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
+-            rdev = atoi(buf+12);
++            rdev = atoi(buf + 12);
+         }
+         memset(buf, 0, ATTR_MAX);
+     }
+     fclose(fp);
+-    goto update_map_file;
+-
+-create_map_file:
+-    ret = local_create_mapped_attr_dir(ctx, path);
+-    if (ret < 0) {
+-        goto err_out;
+-    }
+ 
+ update_map_file:
+-    fp = local_fopen(attr_path, "w");
++    fp = local_fopenat(map_dirfd, name, "w");
++    close_preserve_errno(map_dirfd);
+     if (!fp) {
+-        ret = -1;
+-        goto err_out;
++        return -1;
+     }
+ 
+     if (credp->fc_uid != -1) {
+@@ -230,7 +259,6 @@ update_map_file:
+         rdev = credp->fc_rdev;
+     }
+ 
+-
+     if (uid != -1) {
+         fprintf(fp, "virtfs.uid=%d\n", uid);
+     }
+@@ -245,39 +273,71 @@ update_map_file:
+     }
+     fclose(fp);
+ 
+-err_out:
+-    g_free(attr_path);
++    return 0;
++}
++
++static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
++{
++    int fd, ret;
++
++    /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
++     * Unfortunately, the linux kernel doesn't implement it yet. As an
++     * alternative, let's open the file and use fchmod() instead. This
++     * may fail depending on the permissions of the file, but it is the
++     * best we can do to avoid TOCTTOU. We first try to open read-only
++     * in case name points to a directory. If that fails, we try write-only
++     * in case name doesn't point to a directory.
++     */
++    fd = openat_file(dirfd, name, O_RDONLY, 0);
++    if (fd == -1) {
++        /* In case the file is writable-only and isn't a directory. */
++        if (errno == EACCES) {
++            fd = openat_file(dirfd, name, O_WRONLY, 0);
++        }
++        if (fd == -1 && errno == EISDIR) {
++            errno = EACCES;
++        }
++    }
++    if (fd == -1) {
++        return -1;
++    }
++    ret = fchmod(fd, mode);
++    close_preserve_errno(fd);
+     return ret;
+ }
+ 
+-static int local_set_xattr(const char *path, FsCred *credp)
++static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
+ {
+     int err;
+ 
+     if (credp->fc_uid != -1) {
+         uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
+-        err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid,
++                                   sizeof(uid_t), 0);
+         if (err) {
+             return err;
+         }
+     }
+     if (credp->fc_gid != -1) {
+         uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
+-        err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid,
++                                   sizeof(gid_t), 0);
+         if (err) {
+             return err;
+         }
+     }
+     if (credp->fc_mode != -1) {
+         uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
+-        err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode,
++                                   sizeof(mode_t), 0);
+         if (err) {
+             return err;
+         }
+     }
+     if (credp->fc_rdev != -1) {
+         uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
+-        err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev,
++                                   sizeof(dev_t), 0);
+         if (err) {
+             return err;
+         }
+@@ -285,58 +345,56 @@ static int local_set_xattr(const char *path, FsCred *credp)
+     return 0;
+ }
+ 
+-static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
+-                                         FsCred *credp)
++static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
++                                      const char *name, FsCred *credp)
+ {
+-    char *buffer;
+-
+-    buffer = rpath(fs_ctx, path);
+-    if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
++    if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                 AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) < 0) {
+         /*
+          * If we fail to change ownership and if we are
+          * using security model none. Ignore the error
+          */
+         if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
+-            goto err;
++            return -1;
+         }
+     }
+ 
+-    if (chmod(buffer, credp->fc_mode & 07777) < 0) {
+-        goto err;
+-    }
+-
+-    g_free(buffer);
+-    return 0;
+-err:
+-    g_free(buffer);
+-    return -1;
++    return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
+ }
+ 
+ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
+                               char *buf, size_t bufsz)
+ {
+     ssize_t tsize = -1;
+-    char *buffer;
+-    char *path = fs_path->data;
+ 
+     if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
+         (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
+         int fd;
+-        buffer = rpath(fs_ctx, path);
+-        fd = open(buffer, O_RDONLY | O_NOFOLLOW);
+-        g_free(buffer);
++
++        fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
+         if (fd == -1) {
+             return -1;
+         }
+         do {
+             tsize = read(fd, (void *)buf, bufsz);
+         } while (tsize == -1 && errno == EINTR);
+-        close(fd);
++        close_preserve_errno(fd);
+     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+                (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        tsize = readlink(buffer, buf, bufsz);
+-        g_free(buffer);
++        char *dirpath = g_path_get_dirname(fs_path->data);
++        char *name = g_path_get_basename(fs_path->data);
++        int dirfd;
++
++        dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++        if (dirfd == -1) {
++            goto out;
++        }
++
++        tsize = readlinkat(dirfd, name, buf, bufsz);
++        close_preserve_errno(dirfd);
++    out:
++        g_free(name);
++        g_free(dirpath);
+     }
+     return tsize;
+ }
+@@ -354,27 +412,32 @@ static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+ static int local_open(FsContext *ctx, V9fsPath *fs_path,
+                       int flags, V9fsFidOpenState *fs)
+ {
+-    char *buffer;
+-    char *path = fs_path->data;
++    int fd;
+ 
+-    buffer = rpath(ctx, path);
+-    fs->fd = open(buffer, flags | O_NOFOLLOW);
+-    g_free(buffer);
++    fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
++    if (fd == -1) {
++        return -1;
++    }
++    fs->fd = fd;
+     return fs->fd;
+ }
+ 
+ static int local_opendir(FsContext *ctx,
+                          V9fsPath *fs_path, V9fsFidOpenState *fs)
+ {
+-    char *buffer;
+-    char *path = fs_path->data;
++    int dirfd;
++    DIR *stream;
++
++    dirfd = local_opendir_nofollow(ctx, fs_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
+ 
+-    buffer = rpath(ctx, path);
+-    fs->dir.stream = opendir(buffer);
+-    g_free(buffer);
+-    if (!fs->dir.stream) {
++    stream = fdopendir(dirfd);
++    if (!stream) {
+         return -1;
+     }
++    fs->dir.stream = stream;
+     return 0;
+ }
+ 
+@@ -463,145 +526,122 @@ static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+ 
+ static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
+ {
+-    char *buffer;
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
+     int ret = -1;
+-    char *path = fs_path->data;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
+ 
+     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        buffer = rpath(fs_ctx, path);
+-        ret = local_set_xattr(buffer, credp);
+-        g_free(buffer);
++        ret = local_set_xattrat(dirfd, name, credp);
+     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        return local_set_mapped_file_attr(fs_ctx, path, credp);
+-    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+-               (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        ret = chmod(buffer, credp->fc_mode);
+-        g_free(buffer);
++        ret = local_set_mapped_file_attrat(dirfd, name, credp);
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
+     }
++    close_preserve_errno(dirfd);
++
++out:
++    g_free(dirpath);
++    g_free(name);
+     return ret;
+ }
+ 
+ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
+                        const char *name, FsCred *credp)
+ {
+-    char *path;
+     int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char *buffer = NULL;
++    int dirfd;
+ 
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
+ 
+-    /* Determine the security model */
+-    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        buffer = rpath(fs_ctx, path);
+-        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        err = mknodat(dirfd, name, SM_LOCAL_MODE_BITS | S_IFREG, 0);
+         if (err == -1) {
+             goto out;
+         }
+-        err = local_set_xattr(buffer, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+ 
+-        buffer = rpath(fs_ctx, path);
+-        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
+-        if (err == -1) {
+-            goto out;
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
+         }
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+-    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+-               (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
+         if (err == -1) {
+             goto out;
+         }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+     }
+     goto out;
+ 
+ err_end:
+-    remove(buffer);
+-    errno = serrno;
++    unlinkat_preserve_errno(dirfd, name, 0);
+ out:
+-    g_free(buffer);
+-    v9fs_string_free(&fullname);
++    close_preserve_errno(dirfd);
+     return err;
+ }
+ 
+ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
+                        const char *name, FsCred *credp)
+ {
+-    char *path;
+     int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char *buffer = NULL;
++    int dirfd;
+ 
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
+ 
+-    /* Determine the security model */
+-    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        buffer = rpath(fs_ctx, path);
+-        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        err = mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS);
+         if (err == -1) {
+             goto out;
+         }
+-        credp->fc_mode = credp->fc_mode|S_IFDIR;
+-        err = local_set_xattr(buffer, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        buffer = rpath(fs_ctx, path);
+-        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
+-        if (err == -1) {
+-            goto out;
++        credp->fc_mode = credp->fc_mode | S_IFDIR;
++
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
+         }
+-        credp->fc_mode = credp->fc_mode|S_IFDIR;
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+-    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+-               (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        err = mkdir(buffer, credp->fc_mode);
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = mkdirat(dirfd, name, credp->fc_mode);
+         if (err == -1) {
+             goto out;
+         }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+     }
+     goto out;
+ 
+ err_end:
+-    remove(buffer);
+-    errno = serrno;
++    unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
+ out:
+-    g_free(buffer);
+-    v9fs_string_free(&fullname);
++    close_preserve_errno(dirfd);
+     return err;
+ }
+ 
+@@ -649,62 +689,45 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
+ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
+                        int flags, FsCred *credp, V9fsFidOpenState *fs)
+ {
+-    char *path;
+     int fd = -1;
+     int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char *buffer = NULL;
++    int dirfd;
+ 
+     /*
+      * Mark all the open to not follow symlinks
+      */
+     flags |= O_NOFOLLOW;
+ 
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
+ 
+     /* Determine the security model */
+-    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        buffer = rpath(fs_ctx, path);
+-        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        fd = openat_file(dirfd, name, flags, SM_LOCAL_MODE_BITS);
+         if (fd == -1) {
+-            err = fd;
+             goto out;
+         }
+         credp->fc_mode = credp->fc_mode|S_IFREG;
+-        /* Set cleint credentials in xattr */
+-        err = local_set_xattr(buffer, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        buffer = rpath(fs_ctx, path);
+-        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
+-        if (fd == -1) {
+-            err = fd;
+-            goto out;
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            /* Set cleint credentials in xattr */
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
+         }
+-        credp->fc_mode = credp->fc_mode|S_IFREG;
+-        /* Set client credentials in .virtfs_metadata directory files */
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+                (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        fd = open(buffer, flags, credp->fc_mode);
++        fd = openat_file(dirfd, name, flags, credp->fc_mode);
+         if (fd == -1) {
+-            err = fd;
+             goto out;
+         }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+     }
+@@ -713,12 +736,11 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
+     goto out;
+ 
+ err_end:
+-    close(fd);
+-    remove(buffer);
+-    errno = serrno;
++    unlinkat_preserve_errno(dirfd, name,
++                            flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
++    close_preserve_errno(fd);
+ out:
+-    g_free(buffer);
+-    v9fs_string_free(&fullname);
++    close_preserve_errno(dirfd);
+     return err;
+ }
+ 
+@@ -727,23 +749,22 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+                          V9fsPath *dir_path, const char *name, FsCred *credp)
+ {
+     int err = -1;
+-    int serrno = 0;
+-    char *newpath;
+-    V9fsString fullname;
+-    char *buffer = NULL;
++    int dirfd;
+ 
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    newpath = fullname.data;
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
+ 
+     /* Determine the security model */
+-    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+         int fd;
+         ssize_t oldpath_size, write_size;
+-        buffer = rpath(fs_ctx, newpath);
+-        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
++
++        fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
++                         SM_LOCAL_MODE_BITS);
+         if (fd == -1) {
+-            err = fd;
+             goto out;
+         }
+         /* Write the oldpath (target) to the file. */
+@@ -751,218 +772,204 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+         do {
+             write_size = write(fd, (void *)oldpath, oldpath_size);
+         } while (write_size == -1 && errno == EINTR);
++        close_preserve_errno(fd);
+ 
+         if (write_size != oldpath_size) {
+-            serrno = errno;
+-            close(fd);
+-            err = -1;
+             goto err_end;
+         }
+-        close(fd);
+         /* Set cleint credentials in symlink's xattr */
+-        credp->fc_mode = credp->fc_mode|S_IFLNK;
+-        err = local_set_xattr(buffer, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        int fd;
+-        ssize_t oldpath_size, write_size;
+-        buffer = rpath(fs_ctx, newpath);
+-        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
+-        if (fd == -1) {
+-            err = fd;
+-            goto out;
+-        }
+-        /* Write the oldpath (target) to the file. */
+-        oldpath_size = strlen(oldpath);
+-        do {
+-            write_size = write(fd, (void *)oldpath, oldpath_size);
+-        } while (write_size == -1 && errno == EINTR);
++        credp->fc_mode = credp->fc_mode | S_IFLNK;
+ 
+-        if (write_size != oldpath_size) {
+-            serrno = errno;
+-            close(fd);
+-            err = -1;
+-            goto err_end;
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
+         }
+-        close(fd);
+-        /* Set cleint credentials in symlink's xattr */
+-        credp->fc_mode = credp->fc_mode|S_IFLNK;
+-        err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
+         if (err == -1) {
+-            serrno = errno;
+             goto err_end;
+         }
+-    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+-               (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, newpath);
+-        err = symlink(oldpath, buffer);
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = symlinkat(oldpath, dirfd, name);
+         if (err) {
+             goto out;
+         }
+-        err = lchown(buffer, credp->fc_uid, credp->fc_gid);
++        err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                       AT_SYMLINK_NOFOLLOW);
+         if (err == -1) {
+             /*
+              * If we fail to change ownership and if we are
+              * using security model none. Ignore the error
+              */
+             if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
+-                serrno = errno;
+                 goto err_end;
+-            } else
++            } else {
+                 err = 0;
++            }
+         }
+     }
+     goto out;
+ 
+ err_end:
+-    remove(buffer);
+-    errno = serrno;
++    unlinkat_preserve_errno(dirfd, name, 0);
+ out:
+-    g_free(buffer);
+-    v9fs_string_free(&fullname);
++    close_preserve_errno(dirfd);
+     return err;
+ }
+ 
+ static int local_link(FsContext *ctx, V9fsPath *oldpath,
+                       V9fsPath *dirpath, const char *name)
+ {
+-    int ret;
+-    V9fsString newpath;
+-    char *buffer, *buffer1;
++    char *odirpath = g_path_get_dirname(oldpath->data);
++    char *oname = g_path_get_basename(oldpath->data);
++    int ret = -1;
++    int odirfd, ndirfd;
++
++    odirfd = local_opendir_nofollow(ctx, odirpath);
++    if (odirfd == -1) {
++        goto out;
++    }
+ 
+-    v9fs_string_init(&newpath);
+-    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
++    ndirfd = local_opendir_nofollow(ctx, dirpath->data);
++    if (ndirfd == -1) {
++        close_preserve_errno(odirfd);
++        goto out;
++    }
+ 
+-    buffer = rpath(ctx, oldpath->data);
+-    buffer1 = rpath(ctx, newpath.data);
+-    ret = link(buffer, buffer1);
+-    g_free(buffer);
+-    g_free(buffer1);
++    ret = linkat(odirfd, oname, ndirfd, name, 0);
++    if (ret < 0) {
++        goto out_close;
++    }
+ 
+     /* now link the virtfs_metadata files */
+-    if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
+-        /* Link the .virtfs_metadata files. Create the metada directory */
+-        ret = local_create_mapped_attr_dir(ctx, newpath.data);
+-        if (ret < 0) {
+-            goto err_out;
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int omap_dirfd, nmap_dirfd;
++
++        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
++        if (ret < 0 && errno != EEXIST) {
++            goto err_undo_link;
+         }
+-        buffer = local_mapped_attr_path(ctx, oldpath->data);
+-        buffer1 = local_mapped_attr_path(ctx, newpath.data);
+-        ret = link(buffer, buffer1);
+-        g_free(buffer);
+-        g_free(buffer1);
++
++        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
++        if (omap_dirfd == -1) {
++            goto err;
++        }
++
++        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
++        if (nmap_dirfd == -1) {
++            close_preserve_errno(omap_dirfd);
++            goto err;
++        }
++
++        ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0);
++        close_preserve_errno(nmap_dirfd);
++        close_preserve_errno(omap_dirfd);
+         if (ret < 0 && errno != ENOENT) {
+-            goto err_out;
++            goto err_undo_link;
+         }
+-    }
+-err_out:
+-    v9fs_string_free(&newpath);
+-    return ret;
+-}
+ 
+-static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
+-{
+-    char *buffer;
+-    int ret;
+-    char *path = fs_path->data;
++        ret = 0;
++    }
++    goto out_close;
+ 
+-    buffer = rpath(ctx, path);
+-    ret = truncate(buffer, size);
+-    g_free(buffer);
++err:
++    ret = -1;
++err_undo_link:
++    unlinkat_preserve_errno(ndirfd, name, 0);
++out_close:
++    close_preserve_errno(ndirfd);
++    close_preserve_errno(odirfd);
++out:
++    g_free(oname);
++    g_free(odirpath);
+     return ret;
+ }
+ 
+-static int local_rename(FsContext *ctx, const char *oldpath,
+-                        const char *newpath)
++static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
+ {
+-    int err;
+-    char *buffer, *buffer1;
++    int fd, ret;
+ 
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        err = local_create_mapped_attr_dir(ctx, newpath);
+-        if (err < 0) {
+-            return err;
+-        }
+-        /* rename the .virtfs_metadata files */
+-        buffer = local_mapped_attr_path(ctx, oldpath);
+-        buffer1 = local_mapped_attr_path(ctx, newpath);
+-        err = rename(buffer, buffer1);
+-        g_free(buffer);
+-        g_free(buffer1);
+-        if (err < 0 && errno != ENOENT) {
+-            return err;
+-        }
++    fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
++    if (fd == -1) {
++        return -1;
+     }
+-
+-    buffer = rpath(ctx, oldpath);
+-    buffer1 = rpath(ctx, newpath);
+-    err = rename(buffer, buffer1);
+-    g_free(buffer);
+-    g_free(buffer1);
+-    return err;
++    ret = ftruncate(fd, size);
++    close_preserve_errno(fd);
++    return ret;
+ }
+ 
+ static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
+ {
+-    char *buffer;
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
+     int ret = -1;
+-    char *path = fs_path->data;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
+ 
+     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+         (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+         (fs_ctx->export_flags & V9FS_SM_NONE)) {
+-        buffer = rpath(fs_ctx, path);
+-        ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
+-        g_free(buffer);
++        ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                       AT_SYMLINK_NOFOLLOW);
+     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        buffer = rpath(fs_ctx, path);
+-        ret = local_set_xattr(buffer, credp);
+-        g_free(buffer);
++        ret = local_set_xattrat(dirfd, name, credp);
+     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        return local_set_mapped_file_attr(fs_ctx, path, credp);
++        ret = local_set_mapped_file_attrat(dirfd, name, credp);
+     }
++
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
+     return ret;
+ }
+ 
+ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
+                            const struct timespec *buf)
+ {
+-    char *buffer;
+-    int ret;
+-    char *path = fs_path->data;
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int dirfd, ret = -1;
++
++    dirfd = local_opendir_nofollow(s, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
+ 
+-    buffer = rpath(s, path);
+-    ret = qemu_utimens(buffer, buf);
+-    g_free(buffer);
++    ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(name);
+     return ret;
+ }
+ 
+-static int local_remove(FsContext *ctx, const char *path)
++static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
++                                 int flags)
+ {
+-    int err;
+-    struct stat stbuf;
+-    char *buffer;
++    int ret = -1;
+ 
+     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        buffer = rpath(ctx, path);
+-        err =  lstat(buffer, &stbuf);
+-        g_free(buffer);
+-        if (err) {
+-            goto err_out;
+-        }
+-        /*
+-         * If directory remove .virtfs_metadata contained in the
+-         * directory
+-         */
+-        if (S_ISDIR(stbuf.st_mode)) {
+-            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
+-                                     path, VIRTFS_META_DIR);
+-            err = remove(buffer);
+-            g_free(buffer);
+-            if (err < 0 && errno != ENOENT) {
++        int map_dirfd;
++
++        if (flags == AT_REMOVEDIR) {
++            int fd;
++
++            fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH);
++            if (fd == -1) {
++                goto err_out;
++            }
++            /*
++             * If directory remove .virtfs_metadata contained in the
++             * directory
++             */
++            ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
++            close_preserve_errno(fd);
++            if (ret < 0 && errno != ENOENT) {
+                 /*
+                  * We didn't had the .virtfs_metadata file. May be file created
+                  * in non-mapped mode ?. Ignore ENOENT.
+@@ -972,12 +979,12 @@ static int local_remove(FsContext *ctx, const char *path)
+         }
+         /*
+          * Now remove the name from parent directory
+-         * .virtfs_metadata directory
++         * .virtfs_metadata directory.
+          */
+-        buffer = local_mapped_attr_path(ctx, path);
+-        err = remove(buffer);
+-        g_free(buffer);
+-        if (err < 0 && errno != ENOENT) {
++        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++        ret = unlinkat(map_dirfd, name, 0);
++        close_preserve_errno(map_dirfd);
++        if (ret < 0 && errno != ENOENT) {
+             /*
+              * We didn't had the .virtfs_metadata file. May be file created
+              * in non-mapped mode ?. Ignore ENOENT.
+@@ -986,10 +993,39 @@ static int local_remove(FsContext *ctx, const char *path)
+         }
+     }
+ 
+-    buffer = rpath(ctx, path);
+-    err = remove(buffer);
+-    g_free(buffer);
++    ret = unlinkat(dirfd, name, flags);
++err_out:
++    return ret;
++}
++
++static int local_remove(FsContext *ctx, const char *path)
++{
++    struct stat stbuf;
++    char *dirpath = g_path_get_dirname(path);
++    char *name = g_path_get_basename(path);
++    int flags = 0;
++    int dirfd;
++    int err = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd) {
++        goto out;
++    }
++
++    if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
++        goto err_out;
++    }
++
++    if (S_ISDIR(stbuf.st_mode)) {
++        flags |= AT_REMOVEDIR;
++    }
++
++    err = local_unlinkat_common(ctx, dirfd, name, flags);
+ err_out:
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
+     return err;
+ }
+ 
+@@ -1013,13 +1049,11 @@ static int local_fsync(FsContext *ctx, int fid_type,
+ 
+ static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
+ {
+-    char *buffer;
+-    int ret;
+-    char *path = fs_path->data;
++    int fd, ret;
+ 
+-    buffer = rpath(s, path);
+-    ret = statfs(buffer, stbuf);
+-    g_free(buffer);
++    fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
++    ret = fstatfs(fd, stbuf);
++    close_preserve_errno(fd);
+     return ret;
+ }
+ 
+@@ -1071,70 +1105,105 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
+                           const char *new_name)
+ {
+     int ret;
+-    V9fsString old_full_name, new_full_name;
++    int odirfd, ndirfd;
+ 
+-    v9fs_string_init(&old_full_name);
+-    v9fs_string_init(&new_full_name);
++    odirfd = local_opendir_nofollow(ctx, olddir->data);
++    if (odirfd == -1) {
++        return -1;
++    }
+ 
+-    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
+-    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
++    ndirfd = local_opendir_nofollow(ctx, newdir->data);
++    if (ndirfd == -1) {
++        close_preserve_errno(odirfd);
++        return -1;
++    }
+ 
+-    ret = local_rename(ctx, old_full_name.data, new_full_name.data);
+-    v9fs_string_free(&old_full_name);
+-    v9fs_string_free(&new_full_name);
++    ret = renameat(odirfd, old_name, ndirfd, new_name);
++    if (ret < 0) {
++        goto out;
++    }
++
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int omap_dirfd, nmap_dirfd;
++
++        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
++        if (ret < 0 && errno != EEXIST) {
++            goto err_undo_rename;
++        }
++
++        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
++        if (omap_dirfd == -1) {
++            goto err;
++        }
++
++        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
++        if (nmap_dirfd == -1) {
++            close_preserve_errno(omap_dirfd);
++            goto err;
++        }
++
++        /* rename the .virtfs_metadata files */
++        ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
++        close_preserve_errno(nmap_dirfd);
++        close_preserve_errno(omap_dirfd);
++        if (ret < 0 && errno != ENOENT) {
++            goto err_undo_rename;
++        }
++
++        ret = 0;
++    }
++    goto out;
++
++err:
++    ret = -1;
++err_undo_rename:
++    renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
++out:
++    close_preserve_errno(ndirfd);
++    close_preserve_errno(odirfd);
+     return ret;
+ }
+ 
++static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
++{
++    path->data = g_path_get_dirname(str);
++    path->size = strlen(path->data) + 1;
++}
++
++static int local_rename(FsContext *ctx, const char *oldpath,
++                        const char *newpath)
++{
++    int err;
++    char *oname = g_path_get_basename(oldpath);
++    char *nname = g_path_get_basename(newpath);
++    V9fsPath olddir, newdir;
++
++    v9fs_path_init_dirname(&olddir, oldpath);
++    v9fs_path_init_dirname(&newdir, newpath);
++
++    err = local_renameat(ctx, &olddir, oname, &newdir, nname);
++
++    v9fs_path_free(&newdir);
++    v9fs_path_free(&olddir);
++    g_free(nname);
++    g_free(oname);
++
++    return err;
++}
++
+ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
+                           const char *name, int flags)
+ {
+     int ret;
+-    V9fsString fullname;
+-    char *buffer;
+-
+-    v9fs_string_init(&fullname);
++    int dirfd;
+ 
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        if (flags == AT_REMOVEDIR) {
+-            /*
+-             * If directory remove .virtfs_metadata contained in the
+-             * directory
+-             */
+-            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
+-                                     fullname.data, VIRTFS_META_DIR);
+-            ret = remove(buffer);
+-            g_free(buffer);
+-            if (ret < 0 && errno != ENOENT) {
+-                /*
+-                 * We didn't had the .virtfs_metadata file. May be file created
+-                 * in non-mapped mode ?. Ignore ENOENT.
+-                 */
+-                goto err_out;
+-            }
+-        }
+-        /*
+-         * Now remove the name from parent directory
+-         * .virtfs_metadata directory.
+-         */
+-        buffer = local_mapped_attr_path(ctx, fullname.data);
+-        ret = remove(buffer);
+-        g_free(buffer);
+-        if (ret < 0 && errno != ENOENT) {
+-            /*
+-             * We didn't had the .virtfs_metadata file. May be file created
+-             * in non-mapped mode ?. Ignore ENOENT.
+-             */
+-            goto err_out;
+-        }
++    dirfd = local_opendir_nofollow(ctx, dir->data);
++    if (dirfd == -1) {
++        return -1;
+     }
+-    /* Remove the name finally */
+-    buffer = rpath(ctx, fullname.data);
+-    ret = remove(buffer);
+-    g_free(buffer);
+ 
+-err_out:
+-    v9fs_string_free(&fullname);
++    ret = local_unlinkat_common(ctx, dirfd, name, flags);
++    close_preserve_errno(dirfd);
+     return ret;
+ }
+ 
+@@ -1168,8 +1237,31 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
+ 
+ static int local_init(FsContext *ctx)
+ {
+-    int err = 0;
+     struct statfs stbuf;
++    LocalData *data = g_malloc(sizeof(*data));
++
++    data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
++    if (data->mountfd == -1) {
++        goto err;
++    }
++
++#ifdef FS_IOC_GETVERSION
++    /*
++     * use ioc_getversion only if the ioctl is definied
++     */
++    if (fstatfs(data->mountfd, &stbuf) < 0) {
++        close_preserve_errno(data->mountfd);
++        goto err;
++    }
++    switch (stbuf.f_type) {
++    case EXT2_SUPER_MAGIC:
++    case BTRFS_SUPER_MAGIC:
++    case REISERFS_SUPER_MAGIC:
++    case XFS_SUPER_MAGIC:
++        ctx->exops.get_st_gen = local_ioc_getversion;
++        break;
++    }
++#endif
+ 
+     if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
+         ctx->xops = passthrough_xattr_ops;
+@@ -1185,23 +1277,21 @@ static int local_init(FsContext *ctx)
+         ctx->xops = passthrough_xattr_ops;
+     }
+     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
+-#ifdef FS_IOC_GETVERSION
+-    /*
+-     * use ioc_getversion only if the iocl is definied
+-     */
+-    err = statfs(ctx->fs_root, &stbuf);
+-    if (!err) {
+-        switch (stbuf.f_type) {
+-        case EXT2_SUPER_MAGIC:
+-        case BTRFS_SUPER_MAGIC:
+-        case REISERFS_SUPER_MAGIC:
+-        case XFS_SUPER_MAGIC:
+-            ctx->exops.get_st_gen = local_ioc_getversion;
+-            break;
+-        }
+-    }
+-#endif
+-    return err;
++
++    ctx->private = data;
++    return 0;
++
++err:
++    g_free(data);
++    return -1;
++}
++
++static void local_cleanup(FsContext *ctx)
++{
++    LocalData *data = ctx->private;
++
++    close(data->mountfd);
++    g_free(data);
+ }
+ 
+ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
+@@ -1244,6 +1334,7 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
+ FileOperations local_ops = {
+     .parse_opts = local_parse_opts,
+     .init  = local_init,
++    .cleanup = local_cleanup,
+     .lstat = local_lstat,
+     .readlink = local_readlink,
+     .close = local_close,
+diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
+new file mode 100644
+index 0000000..32c7274
+--- /dev/null
++++ b/hw/9pfs/9p-local.h
+@@ -0,0 +1,20 @@
++/*
++ * 9p local backend utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#ifndef QEMU_9P_LOCAL_H
++#define QEMU_9P_LOCAL_H
++
++int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
++                        mode_t mode);
++int local_opendir_nofollow(FsContext *fs_ctx, const char *path);
++
++#endif
+diff --git a/hw/9pfs/9p-posix-acl.c b/hw/9pfs/9p-posix-acl.c
+index ec00318..bbf8906 100644
+--- a/hw/9pfs/9p-posix-acl.c
++++ b/hw/9pfs/9p-posix-acl.c
+@@ -25,13 +25,7 @@
+ static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
+                                 const char *name, void *value, size_t size)
+ {
+-    char *buffer;
+-    ssize_t ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lgetxattr(buffer, MAP_ACL_ACCESS, value, size);
+-    g_free(buffer);
+-    return ret;
++    return local_getxattr_nofollow(ctx, path, MAP_ACL_ACCESS, value, size);
+ }
+ 
+ static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+@@ -56,23 +50,16 @@ static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+ static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
+                             void *value, size_t size, int flags)
+ {
+-    char *buffer;
+-    int ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lsetxattr(buffer, MAP_ACL_ACCESS, value, size, flags);
+-    g_free(buffer);
+-    return ret;
++    return local_setxattr_nofollow(ctx, path, MAP_ACL_ACCESS, value, size,
++                                   flags);
+ }
+ 
+ static int mp_pacl_removexattr(FsContext *ctx,
+                                const char *path, const char *name)
+ {
+     int ret;
+-    char *buffer;
+ 
+-    buffer = rpath(ctx, path);
+-    ret  = lremovexattr(buffer, MAP_ACL_ACCESS);
++    ret = local_removexattr_nofollow(ctx, path, MAP_ACL_ACCESS);
+     if (ret == -1 && errno == ENODATA) {
+         /*
+          * We don't get ENODATA error when trying to remove a
+@@ -82,20 +69,13 @@ static int mp_pacl_removexattr(FsContext *ctx,
+         errno = 0;
+         ret = 0;
+     }
+-    g_free(buffer);
+     return ret;
+ }
+ 
+ static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
+                                 const char *name, void *value, size_t size)
+ {
+-    char *buffer;
+-    ssize_t ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lgetxattr(buffer, MAP_ACL_DEFAULT, value, size);
+-    g_free(buffer);
+-    return ret;
++    return local_getxattr_nofollow(ctx, path, MAP_ACL_DEFAULT, value, size);
+ }
+ 
+ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+@@ -120,23 +100,16 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+ static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
+                             void *value, size_t size, int flags)
+ {
+-    char *buffer;
+-    int ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lsetxattr(buffer, MAP_ACL_DEFAULT, value, size, flags);
+-    g_free(buffer);
+-    return ret;
++    return local_setxattr_nofollow(ctx, path, MAP_ACL_DEFAULT, value, size,
++                                   flags);
+ }
+ 
+ static int mp_dacl_removexattr(FsContext *ctx,
+                                const char *path, const char *name)
+ {
+     int ret;
+-    char *buffer;
+ 
+-    buffer = rpath(ctx, path);
+-    ret  = lremovexattr(buffer, MAP_ACL_DEFAULT);
++    ret = local_removexattr_nofollow(ctx, path, MAP_ACL_DEFAULT);
+     if (ret == -1 && errno == ENODATA) {
+         /*
+          * We don't get ENODATA error when trying to remove a
+@@ -146,7 +119,6 @@ static int mp_dacl_removexattr(FsContext *ctx,
+         errno = 0;
+         ret = 0;
+     }
+-    g_free(buffer);
+     return ret;
+ }
+ 
+diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c
+new file mode 100644
+index 0000000..fdb4d57
+--- /dev/null
++++ b/hw/9pfs/9p-util.c
+@@ -0,0 +1,69 @@
++/*
++ * 9p utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * 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/xattr.h"
++#include "9p-util.h"
++
++int relative_openat_nofollow(int dirfd, const char *path, int flags,
++                             mode_t mode)
++{
++    int fd;
++
++    fd = dup(dirfd);
++    if (fd == -1) {
++        return -1;
++    }
++
++    while (*path) {
++        const char *c;
++        int next_fd;
++        char *head;
++
++        /* Only relative paths without consecutive slashes */
++        assert(path[0] != '/');
++
++        head = g_strdup(path);
++        c = strchr(path, '/');
++        if (c) {
++            head[c - path] = 0;
++            next_fd = openat_dir(fd, head);
++        } else {
++            next_fd = openat_file(fd, head, flags, mode);
++        }
++        g_free(head);
++        if (next_fd == -1) {
++            close_preserve_errno(fd);
++            return -1;
++        }
++        close(fd);
++        fd = next_fd;
++
++        if (!c) {
++            break;
++        }
++        path = c + 1;
++    }
++
++    return fd;
++}
++
++ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
++                             void *value, size_t size)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lgetxattr(proc_path, name, value, size);
++    g_free(proc_path);
++    return ret;
++}
+diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
+new file mode 100644
+index 0000000..091f3ce
+--- /dev/null
++++ b/hw/9pfs/9p-util.h
+@@ -0,0 +1,54 @@
++/*
++ * 9p utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#ifndef QEMU_9P_UTIL_H
++#define QEMU_9P_UTIL_H
++
++static inline void close_preserve_errno(int fd)
++{
++    int serrno = errno;
++    close(fd);
++    errno = serrno;
++}
++
++static inline int openat_dir(int dirfd, const char *name)
++{
++    return openat(dirfd, name, O_DIRECTORY | O_RDONLY | O_PATH);
++}
++
++static inline int openat_file(int dirfd, const char *name, int flags,
++                              mode_t mode)
++{
++    int fd, serrno, ret;
++
++    fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
++                mode);
++    if (fd == -1) {
++        return -1;
++    }
++
++    serrno = errno;
++    /* O_NONBLOCK was only needed to open the file. Let's drop it. */
++    ret = fcntl(fd, F_SETFL, flags);
++    assert(!ret);
++    errno = serrno;
++    return fd;
++}
++
++int relative_openat_nofollow(int dirfd, const char *path, int flags,
++                             mode_t mode);
++ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
++                             void *value, size_t size);
++int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
++                         void *value, size_t size, int flags);
++
++#endif
+diff --git a/hw/9pfs/9p-xattr-user.c b/hw/9pfs/9p-xattr-user.c
+index f87530c..2c90817 100644
+--- a/hw/9pfs/9p-xattr-user.c
++++ b/hw/9pfs/9p-xattr-user.c
+@@ -20,9 +20,6 @@
+ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+                                 const char *name, void *value, size_t size)
+ {
+-    char *buffer;
+-    ssize_t ret;
+-
+     if (strncmp(name, "user.virtfs.", 12) == 0) {
+         /*
+          * Don't allow fetch of user.virtfs namesapce
+@@ -31,10 +28,7 @@ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+         errno = ENOATTR;
+         return -1;
+     }
+-    buffer = rpath(ctx, path);
+-    ret = lgetxattr(buffer, name, value, size);
+-    g_free(buffer);
+-    return ret;
++    return local_getxattr_nofollow(ctx, path, name, value, size);
+ }
+ 
+ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+@@ -73,9 +67,6 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+ static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+                             void *value, size_t size, int flags)
+ {
+-    char *buffer;
+-    int ret;
+-
+     if (strncmp(name, "user.virtfs.", 12) == 0) {
+         /*
+          * Don't allow fetch of user.virtfs namesapce
+@@ -84,18 +75,12 @@ static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+         errno = EACCES;
+         return -1;
+     }
+-    buffer = rpath(ctx, path);
+-    ret = lsetxattr(buffer, name, value, size, flags);
+-    g_free(buffer);
+-    return ret;
++    return local_setxattr_nofollow(ctx, path, name, value, size, flags);
+ }
+ 
+ static int mp_user_removexattr(FsContext *ctx,
+                                const char *path, const char *name)
+ {
+-    char *buffer;
+-    int ret;
+-
+     if (strncmp(name, "user.virtfs.", 12) == 0) {
+         /*
+          * Don't allow fetch of user.virtfs namesapce
+@@ -104,10 +89,7 @@ static int mp_user_removexattr(FsContext *ctx,
+         errno = EACCES;
+         return -1;
+     }
+-    buffer = rpath(ctx, path);
+-    ret = lremovexattr(buffer, name);
+-    g_free(buffer);
+-    return ret;
++    return local_removexattr_nofollow(ctx, path, name);
+ }
+ 
+ XattrOperations mapped_user_xattr = {
+diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
+index 5d8595e..eec160b 100644
+--- a/hw/9pfs/9p-xattr.c
++++ b/hw/9pfs/9p-xattr.c
+@@ -15,6 +15,8 @@
+ #include "9p.h"
+ #include "fsdev/file-op-9p.h"
+ #include "9p-xattr.h"
++#include "9p-util.h"
++#include "9p-local.h"
+ 
+ 
+ static XattrOperations *get_xattr_operations(XattrOperations **h,
+@@ -58,6 +60,16 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path,
+     return name_size;
+ }
+ 
++static ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
++                                     char *list, size_t size)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = llistxattr(proc_path, list, size);
++    g_free(proc_path);
++    return ret;
++}
+ 
+ /*
+  * Get the list and pass to each layer to find out whether
+@@ -67,24 +79,37 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+                         void *value, size_t vsize)
+ {
+     ssize_t size = 0;
+-    char *buffer;
+     void *ovalue = value;
+     XattrOperations *xops;
+     char *orig_value, *orig_value_start;
+     ssize_t xattr_len, parsed_len = 0, attr_len;
++    char *dirpath, *name;
++    int dirfd;
+ 
+     /* Get the actual len */
+-    buffer = rpath(ctx, path);
+-    xattr_len = llistxattr(buffer, value, 0);
++    dirpath = g_path_get_dirname(path);
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    g_free(dirpath);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    name = g_path_get_basename(path);
++    xattr_len = flistxattrat_nofollow(dirfd, name, value, 0);
+     if (xattr_len <= 0) {
+-        g_free(buffer);
++        g_free(name);
++        close_preserve_errno(dirfd);
+         return xattr_len;
+     }
+ 
+     /* Now fetch the xattr and find the actual size */
+     orig_value = g_malloc(xattr_len);
+-    xattr_len = llistxattr(buffer, orig_value, xattr_len);
+-    g_free(buffer);
++    xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len);
++    g_free(name);
++    close_preserve_errno(dirfd);
++    if (xattr_len < 0) {
++        return -1;
++    }
+ 
+     /* store the orig pointer */
+     orig_value_start = orig_value;
+@@ -143,6 +168,135 @@ int v9fs_remove_xattr(FsContext *ctx,
+ 
+ }
+ 
++ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size)
++{
++    return local_getxattr_nofollow(ctx, path, name, value, size);
++}
++
++int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
++                         void *value, size_t size, int flags)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lsetxattr(proc_path, name, value, size, flags);
++    g_free(proc_path);
++    return ret;
++}
++
++ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size,
++                                int flags)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
++                size_t size, int flags)
++{
++    return local_setxattr_nofollow(ctx, path, name, value, size, flags);
++}
++
++static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
++                                       const char *name)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lremovexattr(proc_path, name);
++    g_free(proc_path);
++    return ret;
++}
++
++ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
++                                   const char *name)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fremovexattrat_nofollow(dirfd, filename, name);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++int pt_removexattr(FsContext *ctx, const char *path, const char *name)
++{
++    return local_removexattr_nofollow(ctx, path, name);
++}
++
++ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
++                        void *value, size_t size)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
++int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size, int flags)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
++ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
++                         void *value, size_t size)
++{
++    return 0;
++}
++
++int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
+ XattrOperations *mapped_xattr_ops[] = {
+     &mapped_user_xattr,
+     &mapped_pacl_xattr,
+diff --git a/hw/9pfs/9p-xattr.h b/hw/9pfs/9p-xattr.h
+index a853ea6..0d83996 100644
+--- a/hw/9pfs/9p-xattr.h
++++ b/hw/9pfs/9p-xattr.h
+@@ -29,6 +29,13 @@ typedef struct xattr_operations
+                        const char *path, const char *name);
+ } XattrOperations;
+ 
++ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size);
++ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size,
++                                int flags);
++ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
++                                   const char *name);
+ 
+ extern XattrOperations mapped_user_xattr;
+ extern XattrOperations passthrough_user_xattr;
+@@ -49,73 +56,21 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
+ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+                           void *value, size_t size, int flags);
+ int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
++
+ ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
+                      size_t size);
+-
+-static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
+-                                  const char *name, void *value, size_t size)
+-{
+-    char *buffer;
+-    ssize_t ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lgetxattr(buffer, name, value, size);
+-    g_free(buffer);
+-    return ret;
+-}
+-
+-static inline int pt_setxattr(FsContext *ctx, const char *path,
+-                              const char *name, void *value,
+-                              size_t size, int flags)
+-{
+-    char *buffer;
+-    int ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lsetxattr(buffer, name, value, size, flags);
+-    g_free(buffer);
+-    return ret;
+-}
+-
+-static inline int pt_removexattr(FsContext *ctx,
+-                                 const char *path, const char *name)
+-{
+-    char *buffer;
+-    int ret;
+-
+-    buffer = rpath(ctx, path);
+-    ret = lremovexattr(path, name);
+-    g_free(buffer);
+-    return ret;
+-}
+-
+-static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
+-                                      const char *name, void *value,
+-                                      size_t size)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
+-
+-static inline int notsup_setxattr(FsContext *ctx, const char *path,
+-                                  const char *name, void *value,
+-                                  size_t size, int flags)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
+-
+-static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
+-                                       char *name, void *value, size_t size)
+-{
+-    return 0;
+-}
+-
+-static inline int notsup_removexattr(FsContext *ctx,
+-                                     const char *path, const char *name)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
++ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size);
++int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
++                size_t size, int flags);
++int pt_removexattr(FsContext *ctx, const char *path, const char *name);
++
++ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
++                        void *value, size_t size);
++int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size, int flags);
++ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
++                         void *value, size_t size);
++int notsup_removexattr(FsContext *ctx, const char *path, const char *name);
+ 
+ #endif
+diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs
+index da0ae0c..32197e6 100644
+--- a/hw/9pfs/Makefile.objs
++++ b/hw/9pfs/Makefile.objs
+@@ -1,4 +1,4 @@
+-common-obj-y  = 9p.o
++common-obj-y  = 9p.o 9p-util.o
+ common-obj-y += 9p-local.o 9p-xattr.o
+ common-obj-y += 9p-xattr-user.o 9p-posix-acl.o
+ common-obj-y += coth.o cofs.o codir.o cofile.o
diff -Nru qemu-2.8+dfsg/debian/patches/char-drop-data-written-to-a-disconnected-pty.patch qemu-2.8+dfsg/debian/patches/char-drop-data-written-to-a-disconnected-pty.patch
--- qemu-2.8+dfsg/debian/patches/char-drop-data-written-to-a-disconnected-pty.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/char-drop-data-written-to-a-disconnected-pty.patch	2017-03-01 11:22:42.000000000 +0300
@@ -0,0 +1,35 @@
+From: Ed Swierk <eswierk@skyportsystems.com>
+Date: Tue, 31 Jan 2017 05:45:29 -0800
+Subject: char: drop data written to a disconnected pty
+Commit-Id: 1c64fdbc8177058802df205f5d7cd65edafa59a8
+
+When a serial port writes data to a pty that's disconnected, drop the
+data and return the length dropped. This avoids triggering pointless
+retries in callers like the 16550A serial_xmit(), and causes
+qemu_chr_fe_write() to write all data to the log file, rather than
+logging only while a pty client like virsh console happens to be
+connected.
+
+Signed-off-by: Ed Swierk <eswierk@skyportsystems.com>
+Message-Id: <1485870329-79428-1-git-send-email-eswierk@skyportsystems.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ qemu-char.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/qemu-char.c b/qemu-char.c
+index 2c9940c..4afa9b0 100644
+--- a/qemu-char.c
++++ b/qemu-char.c
+@@ -1523,7 +1523,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+         /* guest sends data, check for (re-)connect */
+         pty_chr_update_read_handler_locked(chr);
+         if (!s->connected) {
+-            return 0;
++            return len;
+         }
+     }
+     return io_channel_send(s->ioc, buf, len);
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/char-fix-ctrl-a-b-not-working.patch qemu-2.8+dfsg/debian/patches/char-fix-ctrl-a-b-not-working.patch
--- qemu-2.8+dfsg/debian/patches/char-fix-ctrl-a-b-not-working.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/char-fix-ctrl-a-b-not-working.patch	2017-02-28 11:50:26.000000000 +0300
@@ -0,0 +1,89 @@
+From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
+Date: Tue, 10 Jan 2017 12:06:21 +0100
+Subject: [PATCH] char: fix ctrl-a b not working
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Commit-Id: fb5e19d2e1472e96d72d5e4d89c20033f8ab345c
+
+CharDriverState.be should be updated to point to the current
+associated backend.
+
+Fix the regression introduced in the "mux" chardev from commit
+a4afa548fc6dd9842ed86639b4d37d4d1c4ad480.
+
+https://bugs.launchpad.net/bugs/1654137
+
+Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Message-Id: <20170110110621.15287-1-marcandre.lureau@redhat.com>
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ qemu-char.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/qemu-char.c b/qemu-char.c
+index 2c9940c..676944a 100644
+--- a/qemu-char.c
++++ b/qemu-char.c
+@@ -499,7 +499,7 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
+ 
+ static void remove_fd_in_watch(CharDriverState *chr);
+ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
+-static void mux_set_focus(MuxDriver *d, int focus);
++static void mux_set_focus(CharDriverState *chr, int focus);
+ 
+ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+@@ -666,7 +666,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+         case 'c':
+             assert(d->mux_cnt > 0); /* handler registered with first fe */
+             /* Switch to the next registered device */
+-            mux_set_focus(d, (d->focus + 1) % d->mux_cnt);
++            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
+             break;
+         case 't':
+             d->timestamps = !d->timestamps;
+@@ -826,8 +826,10 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
+                              context, true);
+ }
+ 
+-static void mux_set_focus(MuxDriver *d, int focus)
++static void mux_set_focus(CharDriverState *chr, int focus)
+ {
++    MuxDriver *d = chr->opaque;
++
+     assert(focus >= 0);
+     assert(focus < d->mux_cnt);
+ 
+@@ -836,6 +838,7 @@ static void mux_set_focus(MuxDriver *d, int focus)
+     }
+ 
+     d->focus = focus;
++    chr->be = d->backends[focus];
+     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+ }
+ 
+@@ -935,7 +938,9 @@ void qemu_chr_fe_deinit(CharBackend *b)
+ 
+     if (b->chr) {
+         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
+-        b->chr->be = NULL;
++        if (b->chr->be == b) {
++            b->chr->be = NULL;
++        }
+         if (b->chr->is_mux) {
+             MuxDriver *d = b->chr->opaque;
+             d->backends[b->tag] = NULL;
+@@ -999,7 +1004,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
+     }
+ 
+     if (b->chr->is_mux) {
+-        mux_set_focus(b->chr->opaque, b->tag);
++        mux_set_focus(b->chr, b->tag);
+     }
+ }
+ 
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch qemu-2.8+dfsg/debian/patches/cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch
--- qemu-2.8+dfsg/debian/patches/cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch	2017-02-27 21:01:44.000000000 +0300
@@ -0,0 +1,49 @@
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 8 Feb 2017 11:18:36 +0100
+Subject: [PATCH] cirrus: add blit_is_unsafe call to cirrus_bitblt_cputovideo (CVE-2017-2620)
+Commit-Id: 92f2b88cea48c6aeba8de568a45f2ed958f3c298
+Bug-Debian: http://bugs.debian.org/855791
+
+CIRRUS_BLTMODE_MEMSYSSRC blits do NOT check blit destination
+and blit width, at all.  Oops.  Fix it.
+
+Security impact: high.
+
+The missing blit destination check allows to write to host memory.
+Basically same as CVE-2014-8106 for the other blit variants.
+
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/display/cirrus_vga.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
+index 1deb520..b9e7cb1 100644
+--- a/hw/display/cirrus_vga.c
++++ b/hw/display/cirrus_vga.c
+@@ -900,6 +900,10 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+ {
+     int w;
+ 
++    if (blit_is_unsafe(s, true)) {
++        return 0;
++    }
++
+     s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
+     s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+     s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+@@ -925,6 +929,10 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+ 	}
+         s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
+     }
++
++    /* the blit_is_unsafe call above should catch this */
++    assert(s->cirrus_blt_srcpitch <= CIRRUS_BLTBUFSIZE);
++
+     s->cirrus_srcptr = s->cirrus_bltbuf;
+     s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+     cirrus_update_memory_access(s);
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/cirrus-fix-oob-access-issue-CVE-2017-2615.patch qemu-2.8+dfsg/debian/patches/cirrus-fix-oob-access-issue-CVE-2017-2615.patch
--- qemu-2.8+dfsg/debian/patches/cirrus-fix-oob-access-issue-CVE-2017-2615.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/cirrus-fix-oob-access-issue-CVE-2017-2615.patch	2017-02-28 10:05:02.000000000 +0300
@@ -0,0 +1,49 @@
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Wed, 1 Feb 2017 09:35:01 +0100
+Subject: cirrus: fix oob access issue (CVE-2017-2615)
+Commit-Id: 62d4c6bd5263bb8413a06c80144fc678df6dfb64
+Bug-Debian: http://bugs.debian.org/854731
+
+When doing bitblt copy in backward mode, we should minus the
+blt width first just like the adding in the forward mode. This
+can avoid the oob access of the front of vga's vram.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+
+{ kraxel: with backward blits (negative pitch) addr is the topmost
+          address, so check it as-is against vram size ]
+
+Cc: qemu-stable@nongnu.org
+Cc: P J P <ppandit@redhat.com>
+Cc: Laszlo Ersek <lersek@redhat.com>
+Cc: Paolo Bonzini <pbonzini@redhat.com>
+Cc: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Fixes: d3532a0db02296e687711b8cdc7791924efccea0 (CVE-2014-8106)
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: 1485938101-26602-1-git-send-email-kraxel@redhat.com
+Reviewed-by: Laszlo Ersek <lersek@redhat.com>
+---
+ hw/display/cirrus_vga.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
+index bdb092e..3bbe3d5 100644
+--- a/hw/display/cirrus_vga.c
++++ b/hw/display/cirrus_vga.c
+@@ -277,10 +277,9 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
+     }
+     if (pitch < 0) {
+         int64_t min = addr
+-            + ((int64_t)s->cirrus_blt_height-1) * pitch;
+-        int32_t max = addr
+-            + s->cirrus_blt_width;
+-        if (min < 0 || max > s->vga.vram_size) {
++            + ((int64_t)s->cirrus_blt_height - 1) * pitch
++            - s->cirrus_blt_width;
++        if (min < -1 || addr >= s->vga.vram_size) {
+             return true;
+         }
+     } else {
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch qemu-2.8+dfsg/debian/patches/cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch
--- qemu-2.8+dfsg/debian/patches/cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch	2017-02-28 12:41:47.000000000 +0300
@@ -0,0 +1,72 @@
+From: Bruce Rogers <brogers@suse.com>
+Date: Mon, 9 Jan 2017 13:35:20 -0700
+Subject: display: cirrus: ignore source pitch value as needed in
+ blit_is_unsafe
+Commit-Id: 913a87885f589d263e682c2eb6637c6e14538061
+
+Commit 4299b90 added a check which is too broad, given that the source
+pitch value is not required to be initialized for solid fill operations.
+This patch refines the blit_is_unsafe() check to ignore source pitch in
+that case. After applying the above commit as a security patch, we
+noticed the SLES 11 SP4 guest gui failed to initialize properly.
+
+Signed-off-by: Bruce Rogers <brogers@suse.com>
+Message-id: 20170109203520.5619-1-brogers@suse.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/display/cirrus_vga.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
+index bdb092e..379910d 100644
+--- a/hw/display/cirrus_vga.c
++++ b/hw/display/cirrus_vga.c
+@@ -294,7 +294,7 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
+     return false;
+ }
+ 
+-static bool blit_is_unsafe(struct CirrusVGAState *s)
++static bool blit_is_unsafe(struct CirrusVGAState *s, bool dst_only)
+ {
+     /* should be the case, see cirrus_bitblt_start */
+     assert(s->cirrus_blt_width > 0);
+@@ -308,6 +308,9 @@ static bool blit_is_unsafe(struct CirrusVGAState *s)
+                               s->cirrus_blt_dstaddr & s->cirrus_addr_mask)) {
+         return true;
+     }
++    if (dst_only) {
++        return false;
++    }
+     if (blit_region_is_unsafe(s, s->cirrus_blt_srcpitch,
+                               s->cirrus_blt_srcaddr & s->cirrus_addr_mask)) {
+         return true;
+@@ -673,7 +676,7 @@ static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
+ 
+     dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
+ 
+-    if (blit_is_unsafe(s))
++    if (blit_is_unsafe(s, false))
+         return 0;
+ 
+     (*s->cirrus_rop) (s, dst, src,
+@@ -691,7 +694,7 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
+ {
+     cirrus_fill_t rop_func;
+ 
+-    if (blit_is_unsafe(s)) {
++    if (blit_is_unsafe(s, true)) {
+         return 0;
+     }
+     rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+@@ -795,7 +798,7 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
+ 
+ static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+ {
+-    if (blit_is_unsafe(s))
++    if (blit_is_unsafe(s, false))
+         return 0;
+ 
+     return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch qemu-2.8+dfsg/debian/patches/megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch
--- qemu-2.8+dfsg/debian/patches/megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch	2017-02-28 09:23:28.000000000 +0300
@@ -0,0 +1,65 @@
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Mon, 2 Jan 2017 11:03:33 +0100
+Subject: megasas: fix guest-triggered memory leak
+Commit-Id: 765a707000e838c30b18d712fe6cb3dd8e0435f3
+Bug-Debian: http://bugs.debian.org/853996
+
+If the guest sets the sglist size to a value >=2GB, megasas_handle_dcmd
+will return MFI_STAT_MEMORY_NOT_AVAILABLE without freeing the memory.
+Avoid this by returning only the status from map_dcmd, and loading
+cmd->iov_size in the caller.
+
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ hw/scsi/megasas.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
+index 67fc1e7..6233865 100644
+--- a/hw/scsi/megasas.c
++++ b/hw/scsi/megasas.c
+@@ -683,14 +683,14 @@ static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
+         trace_megasas_dcmd_invalid_sge(cmd->index,
+                                        cmd->frame->header.sge_count);
+         cmd->iov_size = 0;
+-        return -1;
++        return -EINVAL;
+     }
+     iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl);
+     iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl);
+     pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), 1);
+     qemu_sglist_add(&cmd->qsg, iov_pa, iov_size);
+     cmd->iov_size = iov_size;
+-    return cmd->iov_size;
++    return 0;
+ }
+ 
+ static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size)
+@@ -1559,19 +1559,20 @@ static const struct dcmd_cmd_tbl_t {
+ 
+ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
+ {
+-    int opcode, len;
++    int opcode;
+     int retval = 0;
++    size_t len;
+     const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
+ 
+     opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+     trace_megasas_handle_dcmd(cmd->index, opcode);
+-    len = megasas_map_dcmd(s, cmd);
+-    if (len < 0) {
++    if (megasas_map_dcmd(s, cmd) < 0) {
+         return MFI_STAT_MEMORY_NOT_AVAILABLE;
+     }
+     while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
+         cmdptr++;
+     }
++    len = cmd->iov_size;
+     if (cmdptr->opcode == -1) {
+         trace_megasas_dcmd_unhandled(cmd->index, opcode, len);
+         retval = megasas_dcmd_dummy(s, cmd);
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/nbd_client-fix-drop_sync-CVE-2017-2630.patch qemu-2.8+dfsg/debian/patches/nbd_client-fix-drop_sync-CVE-2017-2630.patch
--- qemu-2.8+dfsg/debian/patches/nbd_client-fix-drop_sync-CVE-2017-2630.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/nbd_client-fix-drop_sync-CVE-2017-2630.patch	2017-02-28 08:52:30.000000000 +0300
@@ -0,0 +1,39 @@
+From: Eric Blake <eblake@redhat.com>
+To: qemu-devel@nongnu.org
+Date: Mon, 20 Feb 2017 20:42:41 -0600
+Message-Id: <20170221024248.11027-2-eblake@redhat.com>
+Subject: [Qemu-devel] [PATCH v4 1/8] nbd/client: fix drop_sync [CVE-2017-2630]
+Cc: pbonzini@redhat.com, vsementsov@virtuozzo.com, den@virtuozzo.com,
+	qemu-block@nongnu.org
+Bug-Debian: http://bugs.debian.org/855227
+
+From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+
+Comparison symbol is misused. It may lead to memory corruption.
+Introduced in commit 7d3123e.
+
+Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+Message-Id: <20170203154757.36140-6-vsementsov@virtuozzo.com>
+[eblake: add CVE details]
+Signed-off-by: Eric Blake <eblake@redhat.com>
+---
+ nbd/client.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/nbd/client.c b/nbd/client.c
+index ffb0743..0d16cd1 100644
+--- a/nbd/client.c
++++ b/nbd/client.c
+@@ -94,7 +94,7 @@ static ssize_t drop_sync(QIOChannel *ioc, size_t size)
+     char small[1024];
+     char *buffer;
+
+-    buffer = sizeof(small) < size ? small : g_malloc(MIN(65536, size));
++    buffer = sizeof(small) > size ? small : g_malloc(MIN(65536, size));
+     while (size > 0) {
+         ssize_t count = read_sync(ioc, buffer, MIN(65536, size));
+
+-- 
+2.9.3
+
+
diff -Nru qemu-2.8+dfsg/debian/patches/net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch qemu-2.8+dfsg/debian/patches/net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch
--- qemu-2.8+dfsg/debian/patches/net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch	2017-02-28 09:50:15.000000000 +0300
@@ -0,0 +1,65 @@
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Thu, 2 Feb 2017 16:16:24 +0530
+Subject: net: imx: limit buffer descriptor count
+Commit-Id: 81f17e0d435c3db3a3e67e0d32ebf9c98973211f
+Bug-Debian: http://bugs.debian.org/839986
+
+i.MX Fast Ethernet Controller uses buffer descriptors to manage
+data flow to/fro receive & transmit queues. While transmitting
+packets, it could continue to read buffer descriptors if a buffer
+descriptor has length of zero and has crafted values in bd.flags.
+Set an upper limit to number of buffer descriptors.
+
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Signed-off-by: Jason Wang <jasowang@redhat.com>
+---
+ hw/net/imx_fec.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
+index 50c7564..90e6ee3 100644
+--- a/hw/net/imx_fec.c
++++ b/hw/net/imx_fec.c
+@@ -55,6 +55,8 @@
+         } \
+     } while (0)
+ 
++#define IMX_MAX_DESC    1024
++
+ static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
+ {
+     static char tmp[20];
+@@ -402,12 +404,12 @@ static void imx_eth_update(IMXFECState *s)
+ 
+ static void imx_fec_do_tx(IMXFECState *s)
+ {
+-    int frame_size = 0;
++    int frame_size = 0, descnt = 0;
+     uint8_t frame[ENET_MAX_FRAME_SIZE];
+     uint8_t *ptr = frame;
+     uint32_t addr = s->tx_descriptor;
+ 
+-    while (1) {
++    while (descnt++ < IMX_MAX_DESC) {
+         IMXFECBufDesc bd;
+         int len;
+ 
+@@ -453,12 +455,12 @@ static void imx_fec_do_tx(IMXFECState *s)
+ 
+ static void imx_enet_do_tx(IMXFECState *s)
+ {
+-    int frame_size = 0;
++    int frame_size = 0, descnt = 0;
+     uint8_t frame[ENET_MAX_FRAME_SIZE];
+     uint8_t *ptr = frame;
+     uint32_t addr = s->tx_descriptor;
+ 
+-    while (1) {
++    while (descnt++ < IMX_MAX_DESC) {
+         IMXENETBufDesc bd;
+         int len;
+ 
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/s390x-use-qemu-cpu-model-in-user-mode.patch qemu-2.8+dfsg/debian/patches/s390x-use-qemu-cpu-model-in-user-mode.patch
--- qemu-2.8+dfsg/debian/patches/s390x-use-qemu-cpu-model-in-user-mode.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/s390x-use-qemu-cpu-model-in-user-mode.patch	2017-02-28 13:32:28.000000000 +0300
@@ -0,0 +1,34 @@
+From: David Hildenbrand <david@redhat.com>
+Date: Mon, 30 Jan 2017 15:50:25 +0100
+Subject: target/s390x: use "qemu" cpu model in user mode
+Commit-Id: d8923bc75479cd3fdcc72b7647f4877f91950b01
+Bug-Debian: http://bugs.debian.org/854893
+
+"any" does not exist, therefore resulting in a misleading error message.
+
+Reported-by: Stefan Weil <sw@weilnetz.de>
+Signed-off-by: David Hildenbrand <david@redhat.com>
+Message-Id: <20170130145025.26475-1-david@redhat.com>
+Reviewed-by: Stefan Weil <sw@weilnetz.de>
+Reviewed-by: Alexander Graf <agraf@suse.de>
+Cc: qemu-stable@nongnu.org
+---
+ linux-user/main.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/linux-user/main.c b/linux-user/main.c
+index 3004958..e588f58 100644
+--- a/linux-user/main.c
++++ b/linux-user/main.c
+@@ -4322,6 +4322,8 @@ int main(int argc, char **argv, char **envp)
+ # endif
+ #elif defined TARGET_SH4
+         cpu_model = TYPE_SH7785_CPU;
++#elif defined TARGET_S390X
++        cpu_model = "qemu";
+ #else
+         cpu_model = "any";
+ #endif
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch qemu-2.8+dfsg/debian/patches/sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch
--- qemu-2.8+dfsg/debian/patches/sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch	2017-02-28 09:26:02.000000000 +0300
@@ -0,0 +1,38 @@
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Tue, 7 Feb 2017 18:29:59 +0000
+Subject: sd: sdhci: check data length during dma_memory_read
+Commit-Id: 42922105beb14c2fc58185ea022b9f72fb5465e9
+Bug-Debian: http://bugs.debian.org/853996
+
+While doing multi block SDMA transfer in routine
+'sdhci_sdma_transfer_multi_blocks', the 's->fifo_buffer' starting
+index 'begin' and data length 's->data_count' could end up to be same.
+This could lead to an OOB access issue. Correct transfer data length
+to avoid it.
+
+Cc: qemu-stable@nongnu.org
+Reported-by: Jiang Xin <jiangxin1@huawei.com>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
+Message-id: 20170130064736.9236-1-ppandit@redhat.com
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+---
+ hw/sd/sdhci.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
+index 01fbf22..5bd5ab6 100644
+--- a/hw/sd/sdhci.c
++++ b/hw/sd/sdhci.c
+@@ -536,7 +536,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
+                 boundary_count -= block_size - begin;
+             }
+             dma_memory_read(&address_space_memory, s->sdmasysad,
+-                            &s->fifo_buffer[begin], s->data_count);
++                            &s->fifo_buffer[begin], s->data_count - begin);
+             s->sdmasysad += s->data_count - begin;
+             if (s->data_count == block_size) {
+                 for (n = 0; n < block_size; n++) {
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch qemu-2.8+dfsg/debian/patches/sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch
--- qemu-2.8+dfsg/debian/patches/sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch	2017-02-28 09:12:53.000000000 +0300
@@ -0,0 +1,53 @@
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Mon, 27 Feb 2017 18:04:33 +0000
+Subject: sd: sdhci: check transfer mode register in multi block transfer
+Bug-Debian: http://bugs.debian.org/855159
+
+In the SDHCI protocol, the transfer mode register value
+is used during multi block transfer to check if block count
+register is enabled and should be updated. Transfer mode
+register could be set such that, block count register would
+not be updated, thus leading to an infinite loop. Add check
+to avoid it.
+
+Reported-by: Wjjzhang <wjjzhang@tencent.com>
+Reported-by: Jiang Xin <jiangxin1@huawei.com>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Message-id: 20170214185225.7994-3-ppandit@redhat.com
+Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
+Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
+---
+ hw/sd/sdhci.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
+index da32b5f..e3d64e5 100644
+--- a/hw/sd/sdhci.c
++++ b/hw/sd/sdhci.c
+@@ -486,6 +486,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
+     uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
+     uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
+ 
++    if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
++        qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n");
++        return;
++    }
++
+     /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
+      * possible stop at page boundary if initial address is not page aligned,
+      * allow them to work properly */
+@@ -797,11 +802,6 @@ static void sdhci_data_transfer(void *opaque)
+     if (s->trnmod & SDHC_TRNS_DMA) {
+         switch (SDHC_DMA_TYPE(s->hostctl)) {
+         case SDHC_CTRL_SDMA:
+-            if ((s->trnmod & SDHC_TRNS_MULTI) &&
+-                    (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
+-                break;
+-            }
+-
+             if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
+                 sdhci_sdma_transfer_single_block(s);
+             } else {
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch qemu-2.8+dfsg/debian/patches/serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch
--- qemu-2.8+dfsg/debian/patches/serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch	2017-02-21 11:32:25.000000000 +0300
@@ -0,0 +1,41 @@
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Wed, 4 Jan 2017 00:43:16 -0800
+Subject: serial: fix memory leak in serial exit
+Commit-Id: 8409dc884a201bf74b30a9d232b6bbdd00cb7e2b
+Bug-Debian: https://bugs.debian.org/853002
+
+The serial_exit_core function doesn't free some resources.
+This can lead memory leak when hotplug and unplug. This
+patch avoid this.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+Message-Id: <586cb5ab.f31d9d0a.38ac3.acf2@mx.google.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ hw/char/serial.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/hw/char/serial.c b/hw/char/serial.c
+index ffbacd8..67b18ed 100644
+--- a/hw/char/serial.c
++++ b/hw/char/serial.c
+@@ -906,6 +906,16 @@ void serial_realize_core(SerialState *s, Error **errp)
+ void serial_exit_core(SerialState *s)
+ {
+     qemu_chr_fe_deinit(&s->chr);
++
++    timer_del(s->modem_status_poll);
++    timer_free(s->modem_status_poll);
++
++    timer_del(s->fifo_timeout_timer);
++    timer_free(s->fifo_timeout_timer);
++
++    fifo8_destroy(&s->recv_fifo);
++    fifo8_destroy(&s->xmit_fifo);
++
+     qemu_unregister_reset(serial_reset, s);
+ }
+ 
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/series qemu-2.8+dfsg/debian/patches/series
--- qemu-2.8+dfsg/debian/patches/series	2017-01-23 13:07:31.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/series	2017-03-01 12:05:15.000000000 +0300
@@ -2,7 +2,28 @@
 use-fixed-data-path.patch
 use-data-path.patch
 linux-user-fix-s390x-safe-syscall-for-z900.patch
+s390x-use-qemu-cpu-model-in-user-mode.patch
 doc-don-t-mention-memory-it-is-m.patch
+vnc-do-not-disconnect-on-EAGAIN.patch
+xhci-fix-event-queue-IRQ-handling.patch
+xhci-only-free-completed-transfers.patch
+char-fix-ctrl-a-b-not-working.patch
+char-drop-data-written-to-a-disconnected-pty.patch
 audio-ac97-add-exit-function-CVE-2017-5525.patch
 audio-es1370-add-exit-function-CVE-2017-5526.patch
 watchdog-6300esb-add-exit-function-CVE-2016-10155.patch
+serial-fix-memory-leak-in-serial-exit-CVE-2017-5579.patch
+cirrus-ignore-source-pitch-as-needed-in-blit_is_unsafe.patch
+cirrus-add-blit_is_unsafe-to-cirrus_bitblt_cputovideo-CVE-2017-2620.patch
+nbd_client-fix-drop_sync-CVE-2017-2630.patch
+sd-sdhci-check-transfer-mode-register-in-multi-block-CVE-2017-5987.patch
+vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch
+sd-sdhci-check-data-length-during-dma_memory_read-CVE-2017-5667.patch
+megasas-fix-guest-triggered-memory-leak-CVE-2017-5856.patch
+usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch
+virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch
+xhci-apply-limits-to-loops-CVE-2017-5973.patch
+net-imx-limit-buffer-descriptor-count-CVE-2016-7907.patch
+virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch
+cirrus-fix-oob-access-issue-CVE-2017-2615.patch
+9pfs-symlink-attack-fixes-CVE-2016-9602.patch
diff -Nru qemu-2.8+dfsg/debian/patches/usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch qemu-2.8+dfsg/debian/patches/usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch
--- qemu-2.8+dfsg/debian/patches/usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/usb-ccid-check-ccid-apdu-length-CVE-2017-5898.patch	2017-02-28 09:28:39.000000000 +0300
@@ -0,0 +1,36 @@
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Fri, 3 Feb 2017 00:52:28 +0530
+Subject: usb: ccid: check ccid apdu length
+Commit-Id: c7dfbf322595ded4e70b626bf83158a9f3807c6a
+Bug-Debian: http://bugs.debian.org/854729
+
+CCID device emulator uses Application Protocol Data Units(APDU)
+to exchange command and responses to and from the host.
+The length in these units couldn't be greater than 65536. Add
+check to ensure the same. It'd also avoid potential integer
+overflow in emulated_apdu_from_guest.
+
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Message-id: 20170202192228.10847-1-ppandit@redhat.com
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/dev-smartcard-reader.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
+index 89e11b6..1325ea1 100644
+--- a/hw/usb/dev-smartcard-reader.c
++++ b/hw/usb/dev-smartcard-reader.c
+@@ -967,7 +967,7 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
+     DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__,
+                 recv->hdr.bSeq, len);
+     ccid_add_pending_answer(s, (CCID_Header *)recv);
+-    if (s->card) {
++    if (s->card && len <= BULK_OUT_DATA_SIZE) {
+         ccid_card_apdu_from_guest(s->card, recv->abData, len);
+     } else {
+         DPRINTF(s, D_WARN, "warning: discarded apdu\n");
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch qemu-2.8+dfsg/debian/patches/virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch
--- qemu-2.8+dfsg/debian/patches/virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/virtio-crypto-fix-possible-integer-and-heap-overflow-CVE-2017-5931.patch	2017-02-28 09:33:54.000000000 +0300
@@ -0,0 +1,47 @@
+From: Gonglei <arei.gonglei@huawei.com>
+Date: Tue, 3 Jan 2017 14:50:03 +0800
+Subject: virtio-crypto: fix possible integer and heap overflow
+Commit-Id: a08aaff811fb194950f79711d2afe5a892ae03a4
+Bug-Debian: http://bugs.debian.org/854730
+
+Because the 'size_t' type is 4 bytes in 32-bit platform, which
+is the same with 'int'. It's easy to make 'max_len' to zero when
+integer overflow and then cause heap overflow if 'max_len' is zero.
+
+Using uint_64 instead of size_t to avoid the integer overflow.
+
+Cc: qemu-stable@nongnu.org
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Gonglei <arei.gonglei@huawei.com>
+Tested-by: Li Qiang <liqiang6-s@360.cn>
+Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+---
+ hw/virtio/virtio-crypto.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
+index 2f2467e..c23e1ad 100644
+--- a/hw/virtio/virtio-crypto.c
++++ b/hw/virtio/virtio-crypto.c
+@@ -416,7 +416,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev,
+     uint32_t hash_start_src_offset = 0, len_to_hash = 0;
+     uint32_t cipher_start_src_offset = 0, len_to_cipher = 0;
+ 
+-    size_t max_len, curr_size = 0;
++    uint64_t max_len, curr_size = 0;
+     size_t s;
+ 
+     /* Plain cipher */
+@@ -441,7 +441,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev,
+         return NULL;
+     }
+ 
+-    max_len = iv_len + aad_len + src_len + dst_len + hash_result_len;
++    max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len;
+     if (unlikely(max_len > vcrypto->conf.max_size)) {
+         virtio_error(vdev, "virtio-crypto too big length");
+         return NULL;
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch qemu-2.8+dfsg/debian/patches/virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch
--- qemu-2.8+dfsg/debian/patches/virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/virtio-gpu-fix-resource-leak-in-virgl_cmd_resource-CVE-2017-5857.patch	2017-02-28 09:25:36.000000000 +0300
@@ -0,0 +1,49 @@
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Mon, 23 Jan 2017 11:26:50 +0100
+Subject: virtio-gpu: fix resource leak in virgl_cmd_resource_unref
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Commit-Id: 5e8e3c4c75c199aa1017db816fca02be2a9f8798
+Bug-Debian: http://bugs.debian.org/853996
+
+When the guest sends VIRTIO_GPU_CMD_RESOURCE_UNREF without detaching the
+backing storage beforehand (VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING)
+we'll leak memory.
+
+This patch fixes it for 3d mode, simliar to the 2d mode fix in commit
+"b8e2392 virtio-gpu: call cleanup mapping function in resource destroy".
+
+Reported-by: 李强 <liqiang6-s@360.cn>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: 1485167210-4757-1-git-send-email-kraxel@redhat.com
+---
+ hw/display/virtio-gpu-3d.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
+index f96a0c2..ecb09d1 100644
+--- a/hw/display/virtio-gpu-3d.c
++++ b/hw/display/virtio-gpu-3d.c
+@@ -77,10 +77,18 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g,
+                                      struct virtio_gpu_ctrl_command *cmd)
+ {
+     struct virtio_gpu_resource_unref unref;
++    struct iovec *res_iovs = NULL;
++    int num_iovs = 0;
+ 
+     VIRTIO_GPU_FILL_CMD(unref);
+     trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+ 
++    virgl_renderer_resource_detach_iov(unref.resource_id,
++                                       &res_iovs,
++                                       &num_iovs);
++    if (res_iovs != NULL && num_iovs != 0) {
++        virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs);
++    }
+     virgl_renderer_resource_unref(unref.resource_id);
+ }
+ 
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch qemu-2.8+dfsg/debian/patches/vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch
--- qemu-2.8+dfsg/debian/patches/vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/vmxnet3-fix-memory-corruption-on-vlan-header-stripping-CVE-2017-6058.patch	2017-02-28 09:19:32.000000000 +0300
@@ -0,0 +1,122 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [2/5] NetRxPkt: Fix memory corruption on VLAN header stripping
+From: Dmitry Fleytman <dmitry@daynix.com>
+Message-Id: <1487248176-29602-3-git-send-email-dmitry@daynix.com>
+To: qemu-devel@nongnu.org
+Cc: Yan Vugenfirer <yan@daynix.com>, Dmitry Fleytman <dmitry@daynix.com>,
+ Jason Wang <jasowang@redhat.com>, P J P <ppandit@redhat.com>
+Date: Thu, 16 Feb 2017 14:29:33 +0200
+Bug-Debian: http://bugs.debian.org/855616
+
+This patch fixed a problem that was introduced in commit eb700029.
+
+When net_rx_pkt_attach_iovec() calls eth_strip_vlan()
+this can result in pkt->ehdr_buf being overflowed, because
+ehdr_buf is only sizeof(struct eth_header) bytes large
+but eth_strip_vlan() can write
+sizeof(struct eth_header) + sizeof(struct vlan_header)
+bytes into it.
+
+Devices affected by this problem: vmxnet3.
+
+Reported-by: Peter Maydell <peter.maydell@linaro.org>
+Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
+---
+ hw/net/net_rx_pkt.c | 34 +++++++++++++++++-----------------
+ 1 file changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c
+index 1019b50..7c0beac 100644
+--- a/hw/net/net_rx_pkt.c
++++ b/hw/net/net_rx_pkt.c
+@@ -23,13 +23,13 @@
+ 
+ struct NetRxPkt {
+     struct virtio_net_hdr virt_hdr;
+-    uint8_t ehdr_buf[sizeof(struct eth_header)];
++    uint8_t ehdr_buf[sizeof(struct eth_header) + sizeof(struct vlan_header)];
+     struct iovec *vec;
+     uint16_t vec_len_total;
+     uint16_t vec_len;
+     uint32_t tot_len;
+     uint16_t tci;
+-    bool vlan_stripped;
++    size_t ehdr_buf_len;
+     bool has_virt_hdr;
+     eth_pkt_types_e packet_type;
+ 
+@@ -88,15 +88,13 @@ net_rx_pkt_pull_data(struct NetRxPkt *pkt,
+                         const struct iovec *iov, int iovcnt,
+                         size_t ploff)
+ {
+-    if (pkt->vlan_stripped) {
++    if (pkt->ehdr_buf_len) {
+         net_rx_pkt_iovec_realloc(pkt, iovcnt + 1);
+ 
+         pkt->vec[0].iov_base = pkt->ehdr_buf;
+-        pkt->vec[0].iov_len = sizeof(pkt->ehdr_buf);
+-
+-        pkt->tot_len =
+-            iov_size(iov, iovcnt) - ploff + sizeof(struct eth_header);
++        pkt->vec[0].iov_len = pkt->ehdr_buf_len;
+ 
++        pkt->tot_len = iov_size(iov, iovcnt) - ploff + pkt->ehdr_buf_len;
+         pkt->vec_len = iov_copy(pkt->vec + 1, pkt->vec_len_total - 1,
+                                 iov, iovcnt, ploff, pkt->tot_len);
+     } else {
+@@ -123,11 +121,12 @@ void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt,
+     uint16_t tci = 0;
+     uint16_t ploff = iovoff;
+     assert(pkt);
+-    pkt->vlan_stripped = false;
+ 
+     if (strip_vlan) {
+-        pkt->vlan_stripped = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf,
+-                                            &ploff, &tci);
++        pkt->ehdr_buf_len = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf,
++                                           &ploff, &tci);
++    } else {
++        pkt->ehdr_buf_len = 0;
+     }
+ 
+     pkt->tci = tci;
+@@ -143,12 +142,13 @@ void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt,
+     uint16_t tci = 0;
+     uint16_t ploff = iovoff;
+     assert(pkt);
+-    pkt->vlan_stripped = false;
+ 
+     if (strip_vlan) {
+-        pkt->vlan_stripped = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet,
+-                                               pkt->ehdr_buf,
+-                                               &ploff, &tci);
++        pkt->ehdr_buf_len = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet,
++                                              pkt->ehdr_buf,
++                                              &ploff, &tci);
++    } else {
++        pkt->ehdr_buf_len = 0;
+     }
+ 
+     pkt->tci = tci;
+@@ -162,8 +162,8 @@ void net_rx_pkt_dump(struct NetRxPkt *pkt)
+     NetRxPkt *pkt = (NetRxPkt *)pkt;
+     assert(pkt);
+ 
+-    printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n",
+-              pkt->tot_len, pkt->vlan_stripped, pkt->tci);
++    printf("RX PKT: tot_len: %d, ehdr_buf_len: %lu, vlan_tag: %d\n",
++              pkt->tot_len, pkt->ehdr_buf_len, pkt->tci);
+ #endif
+ }
+ 
+@@ -426,7 +426,7 @@ bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt)
+ {
+     assert(pkt);
+ 
+-    return pkt->vlan_stripped;
++    return pkt->ehdr_buf_len ? true : false;
+ }
+ 
+ bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt)
diff -Nru qemu-2.8+dfsg/debian/patches/vnc-do-not-disconnect-on-EAGAIN.patch qemu-2.8+dfsg/debian/patches/vnc-do-not-disconnect-on-EAGAIN.patch
--- qemu-2.8+dfsg/debian/patches/vnc-do-not-disconnect-on-EAGAIN.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/vnc-do-not-disconnect-on-EAGAIN.patch	2017-02-28 10:12:20.000000000 +0300
@@ -0,0 +1,78 @@
+From: Michael Tokarev <mjt@tls.msk.ru>
+Date: Fri, 3 Feb 2017 12:52:29 +0300
+Subject: vnc: do not disconnect on EAGAIN
+Bug-Debian: http://bugs.debian.org/854032
+
+When qemu vnc server is trying to send large update to clients,
+there might be a situation when system responds with something
+like EAGAIN, indicating that there's no system memory to send
+that much data (depending on the network speed, client and server
+and what is happening).  In this case, something like this happens
+on qemu side (from strace):
+
+sendmsg(16, {msg_name(0)=NULL,
+        msg_iov(1)=[{"\244\"..., 729186}],
+        msg_controllen=0, msg_flags=0}, 0) = 103950
+sendmsg(16, {msg_name(0)=NULL,
+        msg_iov(1)=[{"lz\346"..., 1559618}],
+        msg_controllen=0, msg_flags=0}, 0) = -1 EAGAIN
+sendmsg(-1, {msg_name(0)=NULL,
+        msg_iov(1)=[{"lz\346"..., 1559618}],
+        msg_controllen=0, msg_flags=0}, 0) = -1 EBADF
+
+qemu closes the socket before the retry, and obviously it gets EBADF
+when trying to send to -1.
+
+This is because there WAS a special handling for EAGAIN, but now it doesn't
+work anymore, after commit 04d2529da27db512dcbd5e99d0e26d333f16efcc, because
+now in all error-like cases we initiate vnc disconnect.
+
+This change were introduced in qemu 2.6, and caused numerous grief for many
+people, resulting in their vnc clients reporting sporadic random disconnects
+from vnc server.
+
+Fix that by doing the disconnect only when necessary, i.e. omitting this
+very case of EAGAIN.
+
+Hopefully the existing condition (comparing with QIO_CHANNEL_ERR_BLOCK)
+is sufficient, as the original code (before the above commit) were
+checking for other errno values too.
+
+Apparently there's another (semi?)bug exist somewhere here, since the
+code tries to write to fd# -1, it probably should check if the connection
+is open before. But this isn't important.
+
+Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
+Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
+Message-id: 1486115549-9398-1-git-send-email-mjt@msgid.tls.msk.ru
+Fixes: 04d2529da27db512dcbd5e99d0e26d333f16efcc
+Cc: Daniel P. Berrange <berrange@redhat.com>
+Cc: Gerd Hoffmann <kraxel@redhat.com>
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/vnc.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index cdeb79c..f2701e5 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -1256,12 +1256,13 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
+     if (ret <= 0) {
+         if (ret == 0) {
+             VNC_DEBUG("Closing down client sock: EOF\n");
++            vnc_disconnect_start(vs);
+         } else if (ret != QIO_CHANNEL_ERR_BLOCK) {
+             VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
+                       ret, errp ? error_get_pretty(*errp) : "Unknown");
++            vnc_disconnect_start(vs);
+         }
+ 
+-        vnc_disconnect_start(vs);
+         if (errp) {
+             error_free(*errp);
+             *errp = NULL;
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/xhci-apply-limits-to-loops-CVE-2017-5973.patch qemu-2.8+dfsg/debian/patches/xhci-apply-limits-to-loops-CVE-2017-5973.patch
--- qemu-2.8+dfsg/debian/patches/xhci-apply-limits-to-loops-CVE-2017-5973.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/xhci-apply-limits-to-loops-CVE-2017-5973.patch	2017-02-28 09:46:42.000000000 +0300
@@ -0,0 +1,93 @@
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Mon, 6 Feb 2017 13:21:09 +0100
+Subject: xhci: apply limits to loops
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Commit-Id: f89b60f6e5fee3923bedf80e82b4e5efc1bb156b
+Bug-Debian: http://bugs.debian.org/855611
+
+Limits should be big enough that normal guest should not hit it.
+Add a tracepoint to log them, just in case.  Also, while being
+at it, log the existing link trb limit too.
+
+Reported-by: 李强 <liqiang6-s@360.cn>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: 1486383669-6421-1-git-send-email-kraxel@redhat.com
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 4acf0c6..cd0960e 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -54,6 +54,8 @@
+ #define ER_FULL_HACK
+ 
+ #define TRB_LINK_LIMIT  4
++#define COMMAND_LIMIT   256
++#define TRANSFER_LIMIT  256
+ 
+ #define LEN_CAP         0x40
+ #define LEN_OPER        (0x400 + 0x10 * MAXPORTS)
+@@ -1027,6 +1029,7 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
+             return type;
+         } else {
+             if (++link_cnt > TRB_LINK_LIMIT) {
++                trace_usb_xhci_enforced_limit("trb-link");
+                 return 0;
+             }
+             ring->dequeue = xhci_mask64(trb->parameter);
+@@ -2150,6 +2153,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
+     XHCIRing *ring;
+     USBEndpoint *ep = NULL;
+     uint64_t mfindex;
++    unsigned int count = 0;
+     int length;
+     int i;
+ 
+@@ -2258,6 +2262,10 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
+             epctx->retry = xfer;
+             break;
+         }
++        if (count++ > TRANSFER_LIMIT) {
++            trace_usb_xhci_enforced_limit("transfers");
++            break;
++        }
+     }
+ 
+     ep = xhci_epid_to_usbep(epctx);
+@@ -2729,7 +2737,7 @@ static void xhci_process_commands(XHCIState *xhci)
+     TRBType type;
+     XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
+     dma_addr_t addr;
+-    unsigned int i, slotid = 0;
++    unsigned int i, slotid = 0, count = 0;
+ 
+     DPRINTF("xhci_process_commands()\n");
+     if (!xhci_running(xhci)) {
+@@ -2843,6 +2851,11 @@ static void xhci_process_commands(XHCIState *xhci)
+         }
+         event.slotid = slotid;
+         xhci_event(xhci, &event, 0);
++
++        if (count++ > COMMAND_LIMIT) {
++            trace_usb_xhci_enforced_limit("commands");
++            return;
++        }
+     }
+ }
+ 
+diff --git a/hw/usb/trace-events b/hw/usb/trace-events
+index 2d42fd4..a2c85f0 100644
+--- a/hw/usb/trace-events
++++ b/hw/usb/trace-events
+@@ -175,6 +175,7 @@ usb_xhci_xfer_retry(void *xfer) "%p"
+ usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
+ usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
+ usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)"
++usb_xhci_enforced_limit(const char *item) "%s"
+ 
+ # hw/usb/desc.c
+ usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/xhci-fix-event-queue-IRQ-handling.patch qemu-2.8+dfsg/debian/patches/xhci-fix-event-queue-IRQ-handling.patch
--- qemu-2.8+dfsg/debian/patches/xhci-fix-event-queue-IRQ-handling.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/xhci-fix-event-queue-IRQ-handling.patch	2017-02-28 10:25:49.000000000 +0300
@@ -0,0 +1,82 @@
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 3 Feb 2017 07:51:45 +0100
+Subject: [PATCH] xhci: fix event queue IRQ handling
+Commit-Id: 7da76e12cc5cc902dda4c168d8d608fd4e61cbc5
+
+The qemu xhci emulation doesn't handle the ERDP_EHB flag correctly.
+
+When the host adapter queues a new event the ERDP_EHB flag is set.  The
+flag is cleared (via w1c) by the guest when it updates the ERDP (event
+ring dequeue pointer) register to notify the host adapter which events
+it has fetched.
+
+An IRQ must be raised in case the ERDP_EHB flag flips from clear to set.
+If the flag is set already (which implies there are events queued up
+which are not yet processed by the guest) xhci must *not* raise a IRQ.
+
+Qemu got that wrong and raised an IRQ on every event, thereby generating
+spurious interrupts in case we've queued events faster than the guest
+processed them.  This patch fixes that.
+
+With that change in place we also have to check ERDP updates, to see
+whenever the guest has fetched all queued events.  In case there are
+still pending events set ERDP_EHB and raise an IRQ again, to make sure
+the events don't linger unseen forever.
+
+The linux kernel driver and the microsoft windows driver (shipped with
+win8+) can deal with the spurious interrupts without problems.  The
+renesas windows driver (v2.1.39) which can be used on older windows
+versions is quite upset though.  It does spurious ERDP updates now and
+then (not every time, seems we must hit a race window for this to
+happen), which in turn makes the qemu xhci emulation think the event
+ring is full.  Things go south from here ...
+
+tl;dr: This is the "fix xhci on win7" patch.
+
+Cc: M.Cerveny@computer.org
+Cc: 1373228@bugs.launchpad.net
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: 1486104705-13761-1-git-send-email-kraxel@redhat.com
+---
+ hw/usb/hcd-xhci.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 1878dad..54b3901 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -789,11 +789,15 @@ static void xhci_msix_update(XHCIState *xhci, int v)
+ static void xhci_intr_raise(XHCIState *xhci, int v)
+ {
+     PCIDevice *pci_dev = PCI_DEVICE(xhci);
++    bool pending = (xhci->intr[v].erdp_low & ERDP_EHB);
+ 
+     xhci->intr[v].erdp_low |= ERDP_EHB;
+     xhci->intr[v].iman |= IMAN_IP;
+     xhci->usbsts |= USBSTS_EINT;
+ 
++    if (pending) {
++        return;
++    }
+     if (!(xhci->intr[v].iman & IMAN_IE)) {
+         return;
+     }
+@@ -3352,6 +3356,15 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
+             intr->erdp_low &= ~ERDP_EHB;
+         }
+         intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
++        if (val & ERDP_EHB) {
++            dma_addr_t erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
++            unsigned int dp_idx = (erdp - intr->er_start) / TRB_SIZE;
++            if (erdp >= intr->er_start &&
++                erdp < (intr->er_start + TRB_SIZE * intr->er_size) &&
++                dp_idx != intr->er_ep_idx) {
++                xhci_intr_raise(xhci, v);
++            }
++        }
+         break;
+     case 0x1c: /* ERDP high */
+         intr->erdp_high = val;
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/patches/xhci-only-free-completed-transfers.patch qemu-2.8+dfsg/debian/patches/xhci-only-free-completed-transfers.patch
--- qemu-2.8+dfsg/debian/patches/xhci-only-free-completed-transfers.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-2.8+dfsg/debian/patches/xhci-only-free-completed-transfers.patch	2017-02-28 11:38:42.000000000 +0300
@@ -0,0 +1,35 @@
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Mon, 30 Jan 2017 16:36:44 +0100
+Subject: xhci: only free completed transfers
+Commit-Id: f94d18d6c6df388fde196d3ab252f57e33843a8b
+Bug-Debian: http://bugs.debian.org/855659
+
+Most callsites check already, one was missed.
+
+Cc: 1653384@bugs.launchpad.net
+Fixes: 94b037f2a451b3dc855f9f2c346e5049a361bd55
+Reported-by: Fabian Lesniak <fabian@lesniak-it.de>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: 1485790607-31399-2-git-send-email-kraxel@redhat.com
+---
+ hw/usb/hcd-xhci.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index f810678..6a1d3dc 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -2198,7 +2198,9 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
+             xhci_complete_packet(xfer);
+         }
+         assert(!xfer->running_retry);
+-        xhci_ep_free_xfer(epctx->retry);
++        if (xfer->complete) {
++            xhci_ep_free_xfer(epctx->retry);
++        }
+         epctx->retry = NULL;
+     }
+ 
+-- 
+2.1.4
+
diff -Nru qemu-2.8+dfsg/debian/qemu-debootstrap qemu-2.8+dfsg/debian/qemu-debootstrap
--- qemu-2.8+dfsg/debian/qemu-debootstrap	2016-10-26 21:16:27.000000000 +0300
+++ qemu-2.8+dfsg/debian/qemu-debootstrap	2017-02-28 13:55:48.000000000 +0300
@@ -111,6 +111,9 @@
     esac
 done
 
+which debootstrap >/dev/null 2>/dev/null ||
+  die "debootstrap isn't found in \$PATH, is debootstrap package installed?"
+
 needs_qemu="yes"
 if [ "$deb_arch" = "$system_arch" ]; then
     warn "Target architecture is the same as host architecture; disabling QEMU support"
diff -Nru qemu-2.8+dfsg/debian/rules qemu-2.8+dfsg/debian/rules
--- qemu-2.8+dfsg/debian/rules	2017-01-23 13:03:14.000000000 +0300
+++ qemu-2.8+dfsg/debian/rules	2017-02-28 12:18:31.000000000 +0300
@@ -303,8 +303,9 @@
 ifneq (,$(wildcard debian/control-in))
 # only include rules for debian/control if debian/control-in is present
 debian/control: debian/control-in debian/rules
+	echo '# autogenerated file, please edit debian/control-in' > $@.tmp
 	sed -e 's/^:$(shell echo ${VENDOR} | tr '[A-Z]' '[a-z]')://' \
-		-e '/^:[a-z]*:/D' $< > $@.tmp
+		-e '/^:[a-z]*:/D' $< >> $@.tmp
 	mv -f $@.tmp $@
 endif
 

--- End Message ---
--- Begin Message ---
Hi,

On Thu, Mar 02, 2017 at 09:26:38PM +0300, Michael Tokarev wrote:
> Please unblock package qemu

Unblocked by Emilio earlier.

Cheers,

Ivo

--- End Message ---

Reply to: