diff options
| author | Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | 2021-04-07 05:42:35 +0000 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-04-30 14:37:24 +0100 |
| commit | 04267a31cc2b8c1aa38df2347546d3e1ed213c27 (patch) | |
| tree | 3c2e0ae29259196ddb8132c4672d2e0f491872a3 | |
| parent | 4783bb12d2475cf58fbdddedb778fcf316e6d49a (diff) | |
| download | poky-04267a31cc2b8c1aa38df2347546d3e1ed213c27.tar.gz | |
qemu: Fix CVE-2020-35517
CVE:
CVE-2020-35517
(From OE-Core rev: 5b69726fdd959f41dc45019700360fcc164150a9)
Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 51376edb13eed748395ebe1e56081c092565be9b)
Signed-off-by: Anuj Mittal <anuj.mittal@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu.inc | 3 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch | 153 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch | 117 | ||||
| -rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch | 303 |
4 files changed, 576 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu.inc b/meta/recipes-devtools/qemu/qemu.inc index a625809597..f0416b0379 100644 --- a/meta/recipes-devtools/qemu/qemu.inc +++ b/meta/recipes-devtools/qemu/qemu.inc | |||
| @@ -31,6 +31,9 @@ SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \ | |||
| 31 | file://determinism.patch \ | 31 | file://determinism.patch \ |
| 32 | file://0001-tests-meson.build-use-relative-path-to-refer-to-file.patch \ | 32 | file://0001-tests-meson.build-use-relative-path-to-refer-to-file.patch \ |
| 33 | file://CVE-2021-20203.patch \ | 33 | file://CVE-2021-20203.patch \ |
| 34 | file://CVE-2020-35517_1.patch \ | ||
| 35 | file://CVE-2020-35517_2.patch \ | ||
| 36 | file://CVE-2020-35517_3.patch \ | ||
| 34 | " | 37 | " |
| 35 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" | 38 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" |
| 36 | 39 | ||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch new file mode 100644 index 0000000000..73a4cb2064 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | From 8afaaee976965b7fb90ec225a51d60f35c5f173c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 3 | Date: Thu, 4 Feb 2021 15:02:06 +0000 | ||
| 4 | Subject: [PATCH] virtiofsd: extract lo_do_open() from lo_open() | ||
| 5 | |||
| 6 | Both lo_open() and lo_create() have similar code to open a file. Extract | ||
| 7 | a common lo_do_open() function from lo_open() that will be used by | ||
| 8 | lo_create() in a later commit. | ||
| 9 | |||
| 10 | Since lo_do_open() does not otherwise need fuse_req_t req, convert | ||
| 11 | lo_add_fd_mapping() to use struct lo_data *lo instead. | ||
| 12 | |||
| 13 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 14 | Message-Id: <20210204150208.367837-2-stefanha@redhat.com> | ||
| 15 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
| 16 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 17 | |||
| 18 | Upstream-Status: Backport | ||
| 19 | [https://github.com/qemu/qemu/commit/8afaaee976965b7fb90ec225a51d60f35c5f173c] | ||
| 20 | |||
| 21 | CVE: CVE-2020-35517 | ||
| 22 | |||
| 23 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 24 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
| 25 | --- | ||
| 26 | tools/virtiofsd/passthrough_ll.c | 73 +++++++++++++++++++++++++--------------- | ||
| 27 | 1 file changed, 46 insertions(+), 27 deletions(-) | ||
| 28 | |||
| 29 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
| 30 | index 5fb36d9..f14fa51 100644 | ||
| 31 | --- a/tools/virtiofsd/passthrough_ll.c | ||
| 32 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
| 33 | @@ -459,17 +459,17 @@ static void lo_map_remove(struct lo_map *map, size_t key) | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Assumes lo->mutex is held */ | ||
| 37 | -static ssize_t lo_add_fd_mapping(fuse_req_t req, int fd) | ||
| 38 | +static ssize_t lo_add_fd_mapping(struct lo_data *lo, int fd) | ||
| 39 | { | ||
| 40 | struct lo_map_elem *elem; | ||
| 41 | |||
| 42 | - elem = lo_map_alloc_elem(&lo_data(req)->fd_map); | ||
| 43 | + elem = lo_map_alloc_elem(&lo->fd_map); | ||
| 44 | if (!elem) { | ||
| 45 | return -1; | ||
| 46 | } | ||
| 47 | |||
| 48 | elem->fd = fd; | ||
| 49 | - return elem - lo_data(req)->fd_map.elems; | ||
| 50 | + return elem - lo->fd_map.elems; | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Assumes lo->mutex is held */ | ||
| 54 | @@ -1651,6 +1651,38 @@ static void update_open_flags(int writeback, int allow_direct_io, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | +static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
| 59 | + struct fuse_file_info *fi) | ||
| 60 | +{ | ||
| 61 | + char buf[64]; | ||
| 62 | + ssize_t fh; | ||
| 63 | + int fd; | ||
| 64 | + | ||
| 65 | + update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
| 66 | + | ||
| 67 | + sprintf(buf, "%i", inode->fd); | ||
| 68 | + fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
| 69 | + if (fd == -1) { | ||
| 70 | + return errno; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + pthread_mutex_lock(&lo->mutex); | ||
| 74 | + fh = lo_add_fd_mapping(lo, fd); | ||
| 75 | + pthread_mutex_unlock(&lo->mutex); | ||
| 76 | + if (fh == -1) { | ||
| 77 | + close(fd); | ||
| 78 | + return ENOMEM; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + fi->fh = fh; | ||
| 82 | + if (lo->cache == CACHE_NONE) { | ||
| 83 | + fi->direct_io = 1; | ||
| 84 | + } else if (lo->cache == CACHE_ALWAYS) { | ||
| 85 | + fi->keep_cache = 1; | ||
| 86 | + } | ||
| 87 | + return 0; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 91 | mode_t mode, struct fuse_file_info *fi) | ||
| 92 | { | ||
| 93 | @@ -1691,7 +1723,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 94 | ssize_t fh; | ||
| 95 | |||
| 96 | pthread_mutex_lock(&lo->mutex); | ||
| 97 | - fh = lo_add_fd_mapping(req, fd); | ||
| 98 | + fh = lo_add_fd_mapping(lo, fd); | ||
| 99 | pthread_mutex_unlock(&lo->mutex); | ||
| 100 | if (fh == -1) { | ||
| 101 | close(fd); | ||
| 102 | @@ -1892,38 +1924,25 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, | ||
| 103 | |||
| 104 | static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
| 105 | { | ||
| 106 | - int fd; | ||
| 107 | - ssize_t fh; | ||
| 108 | - char buf[64]; | ||
| 109 | struct lo_data *lo = lo_data(req); | ||
| 110 | + struct lo_inode *inode = lo_inode(req, ino); | ||
| 111 | + int err; | ||
| 112 | |||
| 113 | fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, | ||
| 114 | fi->flags); | ||
| 115 | |||
| 116 | - update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
| 117 | - | ||
| 118 | - sprintf(buf, "%i", lo_fd(req, ino)); | ||
| 119 | - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
| 120 | - if (fd == -1) { | ||
| 121 | - return (void)fuse_reply_err(req, errno); | ||
| 122 | - } | ||
| 123 | - | ||
| 124 | - pthread_mutex_lock(&lo->mutex); | ||
| 125 | - fh = lo_add_fd_mapping(req, fd); | ||
| 126 | - pthread_mutex_unlock(&lo->mutex); | ||
| 127 | - if (fh == -1) { | ||
| 128 | - close(fd); | ||
| 129 | - fuse_reply_err(req, ENOMEM); | ||
| 130 | + if (!inode) { | ||
| 131 | + fuse_reply_err(req, EBADF); | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | |||
| 135 | - fi->fh = fh; | ||
| 136 | - if (lo->cache == CACHE_NONE) { | ||
| 137 | - fi->direct_io = 1; | ||
| 138 | - } else if (lo->cache == CACHE_ALWAYS) { | ||
| 139 | - fi->keep_cache = 1; | ||
| 140 | + err = lo_do_open(lo, inode, fi); | ||
| 141 | + lo_inode_put(lo, &inode); | ||
| 142 | + if (err) { | ||
| 143 | + fuse_reply_err(req, err); | ||
| 144 | + } else { | ||
| 145 | + fuse_reply_open(req, fi); | ||
| 146 | } | ||
| 147 | - fuse_reply_open(req, fi); | ||
| 148 | } | ||
| 149 | |||
| 150 | static void lo_release(fuse_req_t req, fuse_ino_t ino, | ||
| 151 | -- | ||
| 152 | 1.8.3.1 | ||
| 153 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch new file mode 100644 index 0000000000..bf11bdb6f8 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | From 22d2ece71e533310da31f2857ebc4a00d91968b3 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 3 | Date: Thu, 4 Feb 2021 15:02:07 +0000 | ||
| 4 | Subject: [PATCH] virtiofsd: optionally return inode pointer from | ||
| 5 | lo_do_lookup() | ||
| 6 | |||
| 7 | lo_do_lookup() finds an existing inode or allocates a new one. It | ||
| 8 | increments nlookup so that the inode stays alive until the client | ||
| 9 | releases it. | ||
| 10 | |||
| 11 | Existing callers don't need the struct lo_inode so the function doesn't | ||
| 12 | return it. Extend the function to optionally return the inode. The next | ||
| 13 | commit will need it. | ||
| 14 | |||
| 15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 16 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
| 17 | Message-Id: <20210204150208.367837-3-stefanha@redhat.com> | ||
| 18 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 19 | |||
| 20 | Upstream-Status: Backport | ||
| 21 | [https://github.com/qemu/qemu/commit/22d2ece71e533310da31f2857ebc4a00d91968b3] | ||
| 22 | |||
| 23 | CVE: CVE-2020-35517 | ||
| 24 | |||
| 25 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 26 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
| 27 | --- | ||
| 28 | tools/virtiofsd/passthrough_ll.c | 29 +++++++++++++++++++++-------- | ||
| 29 | 1 file changed, 21 insertions(+), 8 deletions(-) | ||
| 30 | |||
| 31 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
| 32 | index f14fa51..aa35fc6 100644 | ||
| 33 | --- a/tools/virtiofsd/passthrough_ll.c | ||
| 34 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
| 35 | @@ -831,11 +831,13 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname, | ||
| 36 | } | ||
| 37 | |||
| 38 | /* | ||
| 39 | - * Increments nlookup and caller must release refcount using | ||
| 40 | - * lo_inode_put(&parent). | ||
| 41 | + * Increments nlookup on the inode on success. unref_inode_lolocked() must be | ||
| 42 | + * called eventually to decrement nlookup again. If inodep is non-NULL, the | ||
| 43 | + * inode pointer is stored and the caller must call lo_inode_put(). | ||
| 44 | */ | ||
| 45 | static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 46 | - struct fuse_entry_param *e) | ||
| 47 | + struct fuse_entry_param *e, | ||
| 48 | + struct lo_inode **inodep) | ||
| 49 | { | ||
| 50 | int newfd; | ||
| 51 | int res; | ||
| 52 | @@ -845,6 +847,10 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 53 | struct lo_inode *inode = NULL; | ||
| 54 | struct lo_inode *dir = lo_inode(req, parent); | ||
| 55 | |||
| 56 | + if (inodep) { | ||
| 57 | + *inodep = NULL; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | /* | ||
| 61 | * name_to_handle_at() and open_by_handle_at() can reach here with fuse | ||
| 62 | * mount point in guest, but we don't have its inode info in the | ||
| 63 | @@ -913,7 +919,14 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 64 | pthread_mutex_unlock(&lo->mutex); | ||
| 65 | } | ||
| 66 | e->ino = inode->fuse_ino; | ||
| 67 | - lo_inode_put(lo, &inode); | ||
| 68 | + | ||
| 69 | + /* Transfer ownership of inode pointer to caller or drop it */ | ||
| 70 | + if (inodep) { | ||
| 71 | + *inodep = inode; | ||
| 72 | + } else { | ||
| 73 | + lo_inode_put(lo, &inode); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | lo_inode_put(lo, &dir); | ||
| 77 | |||
| 78 | fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, | ||
| 79 | @@ -948,7 +961,7 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | |||
| 83 | - err = lo_do_lookup(req, parent, name, &e); | ||
| 84 | + err = lo_do_lookup(req, parent, name, &e, NULL); | ||
| 85 | if (err) { | ||
| 86 | fuse_reply_err(req, err); | ||
| 87 | } else { | ||
| 88 | @@ -1056,7 +1069,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, | ||
| 89 | goto out; | ||
| 90 | } | ||
| 91 | |||
| 92 | - saverr = lo_do_lookup(req, parent, name, &e); | ||
| 93 | + saverr = lo_do_lookup(req, parent, name, &e, NULL); | ||
| 94 | if (saverr) { | ||
| 95 | goto out; | ||
| 96 | } | ||
| 97 | @@ -1534,7 +1547,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, | ||
| 98 | |||
| 99 | if (plus) { | ||
| 100 | if (!is_dot_or_dotdot(name)) { | ||
| 101 | - err = lo_do_lookup(req, ino, name, &e); | ||
| 102 | + err = lo_do_lookup(req, ino, name, &e, NULL); | ||
| 103 | if (err) { | ||
| 104 | goto error; | ||
| 105 | } | ||
| 106 | @@ -1732,7 +1745,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 107 | } | ||
| 108 | |||
| 109 | fi->fh = fh; | ||
| 110 | - err = lo_do_lookup(req, parent, name, &e); | ||
| 111 | + err = lo_do_lookup(req, parent, name, &e, NULL); | ||
| 112 | } | ||
| 113 | if (lo->cache == CACHE_NONE) { | ||
| 114 | fi->direct_io = 1; | ||
| 115 | -- | ||
| 116 | 1.8.3.1 | ||
| 117 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch new file mode 100644 index 0000000000..f348f3f2bd --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | From a3fdbbc7f271bff7d53d0501b29d910ece0b3789 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 3 | Date: Thu, 4 Feb 2021 15:02:08 +0000 | ||
| 4 | Subject: [PATCH] virtiofsd: prevent opening of special files (CVE-2020-35517) | ||
| 5 | |||
| 6 | A well-behaved FUSE client does not attempt to open special files with | ||
| 7 | FUSE_OPEN because they are handled on the client side (e.g. device nodes | ||
| 8 | are handled by client-side device drivers). | ||
| 9 | |||
| 10 | The check to prevent virtiofsd from opening special files is missing in | ||
| 11 | a few cases, most notably FUSE_OPEN. A malicious client can cause | ||
| 12 | virtiofsd to open a device node, potentially allowing the guest to | ||
| 13 | escape. This can be exploited by a modified guest device driver. It is | ||
| 14 | not exploitable from guest userspace since the guest kernel will handle | ||
| 15 | special files inside the guest instead of sending FUSE requests. | ||
| 16 | |||
| 17 | This patch fixes this issue by introducing the lo_inode_open() function | ||
| 18 | to check the file type before opening it. This is a short-term solution | ||
| 19 | because it does not prevent a compromised virtiofsd process from opening | ||
| 20 | device nodes on the host. | ||
| 21 | |||
| 22 | Restructure lo_create() to try O_CREAT | O_EXCL first. Note that O_CREAT | ||
| 23 | | O_EXCL does not follow symlinks, so O_NOFOLLOW masking is not | ||
| 24 | necessary here. If the file exists and the user did not specify O_EXCL, | ||
| 25 | open it via lo_do_open(). | ||
| 26 | |||
| 27 | Reported-by: Alex Xu <alex@alxu.ca> | ||
| 28 | Fixes: CVE-2020-35517 | ||
| 29 | Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 30 | Reviewed-by: Vivek Goyal <vgoyal@redhat.com> | ||
| 31 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
| 32 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
| 33 | Message-Id: <20210204150208.367837-4-stefanha@redhat.com> | ||
| 34 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 35 | |||
| 36 | Upstream-Status: Backport | ||
| 37 | [https://github.com/qemu/qemu/commit/a3fdbbc7f271bff7d53d0501b29d910ece0b3789] | ||
| 38 | |||
| 39 | CVE: CVE-2020-35517 | ||
| 40 | |||
| 41 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
| 42 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
| 43 | --- | ||
| 44 | tools/virtiofsd/passthrough_ll.c | 144 ++++++++++++++++++++----------- | ||
| 45 | 1 file changed, 92 insertions(+), 52 deletions(-) | ||
| 46 | |||
| 47 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
| 48 | index aa35fc6ba5a5..147b59338a18 100644 | ||
| 49 | --- a/tools/virtiofsd/passthrough_ll.c | ||
| 50 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
| 51 | @@ -555,6 +555,38 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino) | ||
| 52 | return fd; | ||
| 53 | } | ||
| 54 | |||
| 55 | +/* | ||
| 56 | + * Open a file descriptor for an inode. Returns -EBADF if the inode is not a | ||
| 57 | + * regular file or a directory. | ||
| 58 | + * | ||
| 59 | + * Use this helper function instead of raw openat(2) to prevent security issues | ||
| 60 | + * when a malicious client opens special files such as block device nodes. | ||
| 61 | + * Symlink inodes are also rejected since symlinks must already have been | ||
| 62 | + * traversed on the client side. | ||
| 63 | + */ | ||
| 64 | +static int lo_inode_open(struct lo_data *lo, struct lo_inode *inode, | ||
| 65 | + int open_flags) | ||
| 66 | +{ | ||
| 67 | + g_autofree char *fd_str = g_strdup_printf("%d", inode->fd); | ||
| 68 | + int fd; | ||
| 69 | + | ||
| 70 | + if (!S_ISREG(inode->filetype) && !S_ISDIR(inode->filetype)) { | ||
| 71 | + return -EBADF; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + /* | ||
| 75 | + * The file is a symlink so O_NOFOLLOW must be ignored. We checked earlier | ||
| 76 | + * that the inode is not a special file but if an external process races | ||
| 77 | + * with us then symlinks are traversed here. It is not possible to escape | ||
| 78 | + * the shared directory since it is mounted as "/" though. | ||
| 79 | + */ | ||
| 80 | + fd = openat(lo->proc_self_fd, fd_str, open_flags & ~O_NOFOLLOW); | ||
| 81 | + if (fd < 0) { | ||
| 82 | + return -errno; | ||
| 83 | + } | ||
| 84 | + return fd; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | static void lo_init(void *userdata, struct fuse_conn_info *conn) | ||
| 88 | { | ||
| 89 | struct lo_data *lo = (struct lo_data *)userdata; | ||
| 90 | @@ -684,9 +716,9 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, | ||
| 91 | if (fi) { | ||
| 92 | truncfd = fd; | ||
| 93 | } else { | ||
| 94 | - sprintf(procname, "%i", ifd); | ||
| 95 | - truncfd = openat(lo->proc_self_fd, procname, O_RDWR); | ||
| 96 | + truncfd = lo_inode_open(lo, inode, O_RDWR); | ||
| 97 | if (truncfd < 0) { | ||
| 98 | + errno = -truncfd; | ||
| 99 | goto out_err; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | @@ -848,7 +880,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 103 | struct lo_inode *dir = lo_inode(req, parent); | ||
| 104 | |||
| 105 | if (inodep) { | ||
| 106 | - *inodep = NULL; | ||
| 107 | + *inodep = NULL; /* in case there is an error */ | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | @@ -1664,19 +1696,26 @@ static void update_open_flags(int writeback, int allow_direct_io, | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | +/* | ||
| 116 | + * Open a regular file, set up an fd mapping, and fill out the struct | ||
| 117 | + * fuse_file_info for it. If existing_fd is not negative, use that fd instead | ||
| 118 | + * opening a new one. Takes ownership of existing_fd. | ||
| 119 | + * | ||
| 120 | + * Returns 0 on success or a positive errno. | ||
| 121 | + */ | ||
| 122 | static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
| 123 | - struct fuse_file_info *fi) | ||
| 124 | + int existing_fd, struct fuse_file_info *fi) | ||
| 125 | { | ||
| 126 | - char buf[64]; | ||
| 127 | ssize_t fh; | ||
| 128 | - int fd; | ||
| 129 | + int fd = existing_fd; | ||
| 130 | |||
| 131 | update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
| 132 | |||
| 133 | - sprintf(buf, "%i", inode->fd); | ||
| 134 | - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
| 135 | - if (fd == -1) { | ||
| 136 | - return errno; | ||
| 137 | + if (fd < 0) { | ||
| 138 | + fd = lo_inode_open(lo, inode, fi->flags); | ||
| 139 | + if (fd < 0) { | ||
| 140 | + return -fd; | ||
| 141 | + } | ||
| 142 | } | ||
| 143 | |||
| 144 | pthread_mutex_lock(&lo->mutex); | ||
| 145 | @@ -1699,9 +1738,10 @@ static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
| 146 | static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 147 | mode_t mode, struct fuse_file_info *fi) | ||
| 148 | { | ||
| 149 | - int fd; | ||
| 150 | + int fd = -1; | ||
| 151 | struct lo_data *lo = lo_data(req); | ||
| 152 | struct lo_inode *parent_inode; | ||
| 153 | + struct lo_inode *inode = NULL; | ||
| 154 | struct fuse_entry_param e; | ||
| 155 | int err; | ||
| 156 | struct lo_cred old = {}; | ||
| 157 | @@ -1727,36 +1767,38 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
| 158 | |||
| 159 | update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
| 160 | |||
| 161 | - fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, | ||
| 162 | - mode); | ||
| 163 | + /* Try to create a new file but don't open existing files */ | ||
| 164 | + fd = openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mode); | ||
| 165 | err = fd == -1 ? errno : 0; | ||
| 166 | - lo_restore_cred(&old); | ||
| 167 | |||
| 168 | - if (!err) { | ||
| 169 | - ssize_t fh; | ||
| 170 | + lo_restore_cred(&old); | ||
| 171 | |||
| 172 | - pthread_mutex_lock(&lo->mutex); | ||
| 173 | - fh = lo_add_fd_mapping(lo, fd); | ||
| 174 | - pthread_mutex_unlock(&lo->mutex); | ||
| 175 | - if (fh == -1) { | ||
| 176 | - close(fd); | ||
| 177 | - err = ENOMEM; | ||
| 178 | - goto out; | ||
| 179 | - } | ||
| 180 | + /* Ignore the error if file exists and O_EXCL was not given */ | ||
| 181 | + if (err && (err != EEXIST || (fi->flags & O_EXCL))) { | ||
| 182 | + goto out; | ||
| 183 | + } | ||
| 184 | |||
| 185 | - fi->fh = fh; | ||
| 186 | - err = lo_do_lookup(req, parent, name, &e, NULL); | ||
| 187 | + err = lo_do_lookup(req, parent, name, &e, &inode); | ||
| 188 | + if (err) { | ||
| 189 | + goto out; | ||
| 190 | } | ||
| 191 | - if (lo->cache == CACHE_NONE) { | ||
| 192 | - fi->direct_io = 1; | ||
| 193 | - } else if (lo->cache == CACHE_ALWAYS) { | ||
| 194 | - fi->keep_cache = 1; | ||
| 195 | + | ||
| 196 | + err = lo_do_open(lo, inode, fd, fi); | ||
| 197 | + fd = -1; /* lo_do_open() takes ownership of fd */ | ||
| 198 | + if (err) { | ||
| 199 | + /* Undo lo_do_lookup() nlookup ref */ | ||
| 200 | + unref_inode_lolocked(lo, inode, 1); | ||
| 201 | } | ||
| 202 | |||
| 203 | out: | ||
| 204 | + lo_inode_put(lo, &inode); | ||
| 205 | lo_inode_put(lo, &parent_inode); | ||
| 206 | |||
| 207 | if (err) { | ||
| 208 | + if (fd >= 0) { | ||
| 209 | + close(fd); | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | fuse_reply_err(req, err); | ||
| 213 | } else { | ||
| 214 | fuse_reply_create(req, &e, fi); | ||
| 215 | @@ -1770,7 +1812,6 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, | ||
| 216 | pid_t pid, int *err) | ||
| 217 | { | ||
| 218 | struct lo_inode_plock *plock; | ||
| 219 | - char procname[64]; | ||
| 220 | int fd; | ||
| 221 | |||
| 222 | plock = | ||
| 223 | @@ -1787,12 +1828,10 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, | ||
| 224 | } | ||
| 225 | |||
| 226 | /* Open another instance of file which can be used for ofd locks. */ | ||
| 227 | - sprintf(procname, "%i", inode->fd); | ||
| 228 | - | ||
| 229 | /* TODO: What if file is not writable? */ | ||
| 230 | - fd = openat(lo->proc_self_fd, procname, O_RDWR); | ||
| 231 | - if (fd == -1) { | ||
| 232 | - *err = errno; | ||
| 233 | + fd = lo_inode_open(lo, inode, O_RDWR); | ||
| 234 | + if (fd < 0) { | ||
| 235 | + *err = -fd; | ||
| 236 | free(plock); | ||
| 237 | return NULL; | ||
| 238 | } | ||
| 239 | @@ -1949,7 +1988,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | - err = lo_do_open(lo, inode, fi); | ||
| 244 | + err = lo_do_open(lo, inode, -1, fi); | ||
| 245 | lo_inode_put(lo, &inode); | ||
| 246 | if (err) { | ||
| 247 | fuse_reply_err(req, err); | ||
| 248 | @@ -2014,39 +2053,40 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
| 249 | static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, | ||
| 250 | struct fuse_file_info *fi) | ||
| 251 | { | ||
| 252 | + struct lo_inode *inode = lo_inode(req, ino); | ||
| 253 | + struct lo_data *lo = lo_data(req); | ||
| 254 | int res; | ||
| 255 | int fd; | ||
| 256 | - char *buf; | ||
| 257 | |||
| 258 | fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, | ||
| 259 | (void *)fi); | ||
| 260 | |||
| 261 | - if (!fi) { | ||
| 262 | - struct lo_data *lo = lo_data(req); | ||
| 263 | - | ||
| 264 | - res = asprintf(&buf, "%i", lo_fd(req, ino)); | ||
| 265 | - if (res == -1) { | ||
| 266 | - return (void)fuse_reply_err(req, errno); | ||
| 267 | - } | ||
| 268 | + if (!inode) { | ||
| 269 | + fuse_reply_err(req, EBADF); | ||
| 270 | + return; | ||
| 271 | + } | ||
| 272 | |||
| 273 | - fd = openat(lo->proc_self_fd, buf, O_RDWR); | ||
| 274 | - free(buf); | ||
| 275 | - if (fd == -1) { | ||
| 276 | - return (void)fuse_reply_err(req, errno); | ||
| 277 | + if (!fi) { | ||
| 278 | + fd = lo_inode_open(lo, inode, O_RDWR); | ||
| 279 | + if (fd < 0) { | ||
| 280 | + res = -fd; | ||
| 281 | + goto out; | ||
| 282 | } | ||
| 283 | } else { | ||
| 284 | fd = lo_fi_fd(req, fi); | ||
| 285 | } | ||
| 286 | |||
| 287 | if (datasync) { | ||
| 288 | - res = fdatasync(fd); | ||
| 289 | + res = fdatasync(fd) == -1 ? errno : 0; | ||
| 290 | } else { | ||
| 291 | - res = fsync(fd); | ||
| 292 | + res = fsync(fd) == -1 ? errno : 0; | ||
| 293 | } | ||
| 294 | if (!fi) { | ||
| 295 | close(fd); | ||
| 296 | } | ||
| 297 | - fuse_reply_err(req, res == -1 ? errno : 0); | ||
| 298 | +out: | ||
| 299 | + lo_inode_put(lo, &inode); | ||
| 300 | + fuse_reply_err(req, res); | ||
| 301 | } | ||
| 302 | |||
| 303 | static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, | ||
