From 4122d7341425209be538c71dffed6043659d683b Mon Sep 17 00:00:00 2001 From: Andreas Wellving Date: Mon, 4 Feb 2019 14:05:29 +0100 Subject: vhost/vsock: CVE-2018-14625 vhost/vsock: fix use-after-free in network stack callers References: https://nvd.nist.gov/vuln/detail/CVE-2018-14625 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-4.14.y&id=f15c072d6576c5e2b693c22e39ccc9103c952078 Change-Id: Ica09a6a909b9276f3d8ba1d0980e2e6473d461d7 Signed-off-by: Andreas Wellving --- patches/cve/4.14.x.scc | 3 + ...-fix-use-after-free-in-network-stack-call.patch | 199 +++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 patches/cve/CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch diff --git a/patches/cve/4.14.x.scc b/patches/cve/4.14.x.scc index 59a57b6..e1a218f 100644 --- a/patches/cve/4.14.x.scc +++ b/patches/cve/4.14.x.scc @@ -13,3 +13,6 @@ patch CVE-2018-18690-xfs-don-t-fail-when-converting-shortform-attr-to-lon.patch patch CVE-2018-19407-KVM-X86-Fix-scan-ioapic-use-before-initialization.patch #CVEs fixed in 4.14.87: patch CVE-2018-18397-userfaultfd-use-ENOENT-instead-of-EFAULT-if-the-atom.patch +#CVEs fixed in 4.14.88: +patch CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch + diff --git a/patches/cve/CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch b/patches/cve/CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch new file mode 100644 index 0000000..d51b3c8 --- /dev/null +++ b/patches/cve/CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch @@ -0,0 +1,199 @@ +From f15c072d6576c5e2b693c22e39ccc9103c952078 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Mon, 5 Nov 2018 10:35:47 +0000 +Subject: [PATCH] vhost/vsock: fix use-after-free in network stack callers + +commit 834e772c8db0c6a275d75315d90aba4ebbb1e249 upstream. + +If the network stack calls .send_pkt()/.cancel_pkt() during .release(), +a struct vhost_vsock use-after-free is possible. This occurs because +.release() does not wait for other CPUs to stop using struct +vhost_vsock. + +Switch to an RCU-enabled hashtable (indexed by guest CID) so that +.release() can wait for other CPUs by calling synchronize_rcu(). This +also eliminates vhost_vsock_lock acquisition in the data path so it +could have a positive effect on performance. + +This is CVE-2018-14625 "kernel: use-after-free Read in vhost_transport_send_pkt". + +CVE: CVE-2018-14625 +Upstream-Status: Backport + +Cc: stable@vger.kernel.org +Reported-and-tested-by: syzbot+bd391451452fb0b93039@syzkaller.appspotmail.com +Reported-by: syzbot+e3e074963495f92a89ed@syzkaller.appspotmail.com +Reported-by: syzbot+d5a0a170c5069658b141@syzkaller.appspotmail.com +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Michael S. Tsirkin +Acked-by: Jason Wang +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Andreas Wellving +--- + drivers/vhost/vsock.c | 57 +++++++++++++++++++++++++------------------ + 1 file changed, 33 insertions(+), 24 deletions(-) + +diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c +index c9de9c41aa97..b044a0800805 100644 +--- a/drivers/vhost/vsock.c ++++ b/drivers/vhost/vsock.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + #include "vhost.h" +@@ -27,14 +28,14 @@ enum { + + /* Used to track all the vhost_vsock instances on the system. */ + static DEFINE_SPINLOCK(vhost_vsock_lock); +-static LIST_HEAD(vhost_vsock_list); ++static DEFINE_READ_MOSTLY_HASHTABLE(vhost_vsock_hash, 8); + + struct vhost_vsock { + struct vhost_dev dev; + struct vhost_virtqueue vqs[2]; + +- /* Link to global vhost_vsock_list, protected by vhost_vsock_lock */ +- struct list_head list; ++ /* Link to global vhost_vsock_hash, writes use vhost_vsock_lock */ ++ struct hlist_node hash; + + struct vhost_work send_pkt_work; + spinlock_t send_pkt_list_lock; +@@ -50,11 +51,14 @@ static u32 vhost_transport_get_local_cid(void) + return VHOST_VSOCK_DEFAULT_HOST_CID; + } + +-static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid) ++/* Callers that dereference the return value must hold vhost_vsock_lock or the ++ * RCU read lock. ++ */ ++static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) + { + struct vhost_vsock *vsock; + +- list_for_each_entry(vsock, &vhost_vsock_list, list) { ++ hash_for_each_possible_rcu(vhost_vsock_hash, vsock, hash, guest_cid) { + u32 other_cid = vsock->guest_cid; + + /* Skip instances that have no CID yet */ +@@ -69,17 +73,6 @@ static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid) + return NULL; + } + +-static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) +-{ +- struct vhost_vsock *vsock; +- +- spin_lock_bh(&vhost_vsock_lock); +- vsock = __vhost_vsock_get(guest_cid); +- spin_unlock_bh(&vhost_vsock_lock); +- +- return vsock; +-} +- + static void + vhost_transport_do_send_pkt(struct vhost_vsock *vsock, + struct vhost_virtqueue *vq) +@@ -210,9 +203,12 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) + struct vhost_vsock *vsock; + int len = pkt->len; + ++ rcu_read_lock(); ++ + /* Find the vhost_vsock according to guest context id */ + vsock = vhost_vsock_get(le64_to_cpu(pkt->hdr.dst_cid)); + if (!vsock) { ++ rcu_read_unlock(); + virtio_transport_free_pkt(pkt); + return -ENODEV; + } +@@ -225,6 +221,8 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) + spin_unlock_bh(&vsock->send_pkt_list_lock); + + vhost_work_queue(&vsock->dev, &vsock->send_pkt_work); ++ ++ rcu_read_unlock(); + return len; + } + +@@ -234,12 +232,15 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk) + struct vhost_vsock *vsock; + struct virtio_vsock_pkt *pkt, *n; + int cnt = 0; ++ int ret = -ENODEV; + LIST_HEAD(freeme); + ++ rcu_read_lock(); ++ + /* Find the vhost_vsock according to guest context id */ + vsock = vhost_vsock_get(vsk->remote_addr.svm_cid); + if (!vsock) +- return -ENODEV; ++ goto out; + + spin_lock_bh(&vsock->send_pkt_list_lock); + list_for_each_entry_safe(pkt, n, &vsock->send_pkt_list, list) { +@@ -265,7 +266,10 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk) + vhost_poll_queue(&tx_vq->poll); + } + +- return 0; ++ ret = 0; ++out: ++ rcu_read_unlock(); ++ return ret; + } + + static struct virtio_vsock_pkt * +@@ -531,10 +535,6 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file) + spin_lock_init(&vsock->send_pkt_list_lock); + INIT_LIST_HEAD(&vsock->send_pkt_list); + vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work); +- +- spin_lock_bh(&vhost_vsock_lock); +- list_add_tail(&vsock->list, &vhost_vsock_list); +- spin_unlock_bh(&vhost_vsock_lock); + return 0; + + out: +@@ -575,9 +575,13 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file) + struct vhost_vsock *vsock = file->private_data; + + spin_lock_bh(&vhost_vsock_lock); +- list_del(&vsock->list); ++ if (vsock->guest_cid) ++ hash_del_rcu(&vsock->hash); + spin_unlock_bh(&vhost_vsock_lock); + ++ /* Wait for other CPUs to finish using vsock */ ++ synchronize_rcu(); ++ + /* Iterating over all connections for all CIDs to find orphans is + * inefficient. Room for improvement here. */ + vsock_for_each_connected_socket(vhost_vsock_reset_orphans); +@@ -618,12 +622,17 @@ static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid) + + /* Refuse if CID is already in use */ + spin_lock_bh(&vhost_vsock_lock); +- other = __vhost_vsock_get(guest_cid); ++ other = vhost_vsock_get(guest_cid); + if (other && other != vsock) { + spin_unlock_bh(&vhost_vsock_lock); + return -EADDRINUSE; + } ++ ++ if (vsock->guest_cid) ++ hash_del_rcu(&vsock->hash); ++ + vsock->guest_cid = guest_cid; ++ hash_add_rcu(vhost_vsock_hash, &vsock->hash, guest_cid); + spin_unlock_bh(&vhost_vsock_lock); + + return 0; +-- +2.19.2 + -- cgit v1.2.3-54-g00ecf