diff options
author | Andreas Wellving <andreas.wellving@enea.com> | 2019-02-04 14:05:29 +0100 |
---|---|---|
committer | Andreas Wellving <andreas.wellving@enea.com> | 2019-02-04 14:05:29 +0100 |
commit | 4122d7341425209be538c71dffed6043659d683b (patch) | |
tree | 94b45930f1aee97ffde5967f119737bc616fd06b | |
parent | b6643e162871f2c81cc342f510ac9bd35e7744bd (diff) | |
download | enea-kernel-cache-4122d7341425209be538c71dffed6043659d683b.tar.gz |
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 <andreas.wellving@enea.com>
-rw-r--r-- | patches/cve/4.14.x.scc | 3 | ||||
-rw-r--r-- | patches/cve/CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch | 199 |
2 files changed, 202 insertions, 0 deletions
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 | |||
13 | patch CVE-2018-19407-KVM-X86-Fix-scan-ioapic-use-before-initialization.patch | 13 | patch CVE-2018-19407-KVM-X86-Fix-scan-ioapic-use-before-initialization.patch |
14 | #CVEs fixed in 4.14.87: | 14 | #CVEs fixed in 4.14.87: |
15 | patch CVE-2018-18397-userfaultfd-use-ENOENT-instead-of-EFAULT-if-the-atom.patch | 15 | patch CVE-2018-18397-userfaultfd-use-ENOENT-instead-of-EFAULT-if-the-atom.patch |
16 | #CVEs fixed in 4.14.88: | ||
17 | patch CVE-2018-14625-vhost-vsock-fix-use-after-free-in-network-stack-call.patch | ||
18 | |||
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 @@ | |||
1 | From f15c072d6576c5e2b693c22e39ccc9103c952078 Mon Sep 17 00:00:00 2001 | ||
2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
3 | Date: Mon, 5 Nov 2018 10:35:47 +0000 | ||
4 | Subject: [PATCH] vhost/vsock: fix use-after-free in network stack callers | ||
5 | |||
6 | commit 834e772c8db0c6a275d75315d90aba4ebbb1e249 upstream. | ||
7 | |||
8 | If the network stack calls .send_pkt()/.cancel_pkt() during .release(), | ||
9 | a struct vhost_vsock use-after-free is possible. This occurs because | ||
10 | .release() does not wait for other CPUs to stop using struct | ||
11 | vhost_vsock. | ||
12 | |||
13 | Switch to an RCU-enabled hashtable (indexed by guest CID) so that | ||
14 | .release() can wait for other CPUs by calling synchronize_rcu(). This | ||
15 | also eliminates vhost_vsock_lock acquisition in the data path so it | ||
16 | could have a positive effect on performance. | ||
17 | |||
18 | This is CVE-2018-14625 "kernel: use-after-free Read in vhost_transport_send_pkt". | ||
19 | |||
20 | CVE: CVE-2018-14625 | ||
21 | Upstream-Status: Backport | ||
22 | |||
23 | Cc: stable@vger.kernel.org | ||
24 | Reported-and-tested-by: syzbot+bd391451452fb0b93039@syzkaller.appspotmail.com | ||
25 | Reported-by: syzbot+e3e074963495f92a89ed@syzkaller.appspotmail.com | ||
26 | Reported-by: syzbot+d5a0a170c5069658b141@syzkaller.appspotmail.com | ||
27 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
28 | Signed-off-by: Michael S. Tsirkin <mst@redhat.com> | ||
29 | Acked-by: Jason Wang <jasowang@redhat.com> | ||
30 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
31 | Signed-off-by: Andreas Wellving <andreas.wellving@enea.com> | ||
32 | --- | ||
33 | drivers/vhost/vsock.c | 57 +++++++++++++++++++++++++------------------ | ||
34 | 1 file changed, 33 insertions(+), 24 deletions(-) | ||
35 | |||
36 | diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c | ||
37 | index c9de9c41aa97..b044a0800805 100644 | ||
38 | --- a/drivers/vhost/vsock.c | ||
39 | +++ b/drivers/vhost/vsock.c | ||
40 | @@ -15,6 +15,7 @@ | ||
41 | #include <net/sock.h> | ||
42 | #include <linux/virtio_vsock.h> | ||
43 | #include <linux/vhost.h> | ||
44 | +#include <linux/hashtable.h> | ||
45 | |||
46 | #include <net/af_vsock.h> | ||
47 | #include "vhost.h" | ||
48 | @@ -27,14 +28,14 @@ enum { | ||
49 | |||
50 | /* Used to track all the vhost_vsock instances on the system. */ | ||
51 | static DEFINE_SPINLOCK(vhost_vsock_lock); | ||
52 | -static LIST_HEAD(vhost_vsock_list); | ||
53 | +static DEFINE_READ_MOSTLY_HASHTABLE(vhost_vsock_hash, 8); | ||
54 | |||
55 | struct vhost_vsock { | ||
56 | struct vhost_dev dev; | ||
57 | struct vhost_virtqueue vqs[2]; | ||
58 | |||
59 | - /* Link to global vhost_vsock_list, protected by vhost_vsock_lock */ | ||
60 | - struct list_head list; | ||
61 | + /* Link to global vhost_vsock_hash, writes use vhost_vsock_lock */ | ||
62 | + struct hlist_node hash; | ||
63 | |||
64 | struct vhost_work send_pkt_work; | ||
65 | spinlock_t send_pkt_list_lock; | ||
66 | @@ -50,11 +51,14 @@ static u32 vhost_transport_get_local_cid(void) | ||
67 | return VHOST_VSOCK_DEFAULT_HOST_CID; | ||
68 | } | ||
69 | |||
70 | -static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid) | ||
71 | +/* Callers that dereference the return value must hold vhost_vsock_lock or the | ||
72 | + * RCU read lock. | ||
73 | + */ | ||
74 | +static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) | ||
75 | { | ||
76 | struct vhost_vsock *vsock; | ||
77 | |||
78 | - list_for_each_entry(vsock, &vhost_vsock_list, list) { | ||
79 | + hash_for_each_possible_rcu(vhost_vsock_hash, vsock, hash, guest_cid) { | ||
80 | u32 other_cid = vsock->guest_cid; | ||
81 | |||
82 | /* Skip instances that have no CID yet */ | ||
83 | @@ -69,17 +73,6 @@ static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid) | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | -static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) | ||
88 | -{ | ||
89 | - struct vhost_vsock *vsock; | ||
90 | - | ||
91 | - spin_lock_bh(&vhost_vsock_lock); | ||
92 | - vsock = __vhost_vsock_get(guest_cid); | ||
93 | - spin_unlock_bh(&vhost_vsock_lock); | ||
94 | - | ||
95 | - return vsock; | ||
96 | -} | ||
97 | - | ||
98 | static void | ||
99 | vhost_transport_do_send_pkt(struct vhost_vsock *vsock, | ||
100 | struct vhost_virtqueue *vq) | ||
101 | @@ -210,9 +203,12 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) | ||
102 | struct vhost_vsock *vsock; | ||
103 | int len = pkt->len; | ||
104 | |||
105 | + rcu_read_lock(); | ||
106 | + | ||
107 | /* Find the vhost_vsock according to guest context id */ | ||
108 | vsock = vhost_vsock_get(le64_to_cpu(pkt->hdr.dst_cid)); | ||
109 | if (!vsock) { | ||
110 | + rcu_read_unlock(); | ||
111 | virtio_transport_free_pkt(pkt); | ||
112 | return -ENODEV; | ||
113 | } | ||
114 | @@ -225,6 +221,8 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) | ||
115 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
116 | |||
117 | vhost_work_queue(&vsock->dev, &vsock->send_pkt_work); | ||
118 | + | ||
119 | + rcu_read_unlock(); | ||
120 | return len; | ||
121 | } | ||
122 | |||
123 | @@ -234,12 +232,15 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk) | ||
124 | struct vhost_vsock *vsock; | ||
125 | struct virtio_vsock_pkt *pkt, *n; | ||
126 | int cnt = 0; | ||
127 | + int ret = -ENODEV; | ||
128 | LIST_HEAD(freeme); | ||
129 | |||
130 | + rcu_read_lock(); | ||
131 | + | ||
132 | /* Find the vhost_vsock according to guest context id */ | ||
133 | vsock = vhost_vsock_get(vsk->remote_addr.svm_cid); | ||
134 | if (!vsock) | ||
135 | - return -ENODEV; | ||
136 | + goto out; | ||
137 | |||
138 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
139 | list_for_each_entry_safe(pkt, n, &vsock->send_pkt_list, list) { | ||
140 | @@ -265,7 +266,10 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk) | ||
141 | vhost_poll_queue(&tx_vq->poll); | ||
142 | } | ||
143 | |||
144 | - return 0; | ||
145 | + ret = 0; | ||
146 | +out: | ||
147 | + rcu_read_unlock(); | ||
148 | + return ret; | ||
149 | } | ||
150 | |||
151 | static struct virtio_vsock_pkt * | ||
152 | @@ -531,10 +535,6 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file) | ||
153 | spin_lock_init(&vsock->send_pkt_list_lock); | ||
154 | INIT_LIST_HEAD(&vsock->send_pkt_list); | ||
155 | vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work); | ||
156 | - | ||
157 | - spin_lock_bh(&vhost_vsock_lock); | ||
158 | - list_add_tail(&vsock->list, &vhost_vsock_list); | ||
159 | - spin_unlock_bh(&vhost_vsock_lock); | ||
160 | return 0; | ||
161 | |||
162 | out: | ||
163 | @@ -575,9 +575,13 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file) | ||
164 | struct vhost_vsock *vsock = file->private_data; | ||
165 | |||
166 | spin_lock_bh(&vhost_vsock_lock); | ||
167 | - list_del(&vsock->list); | ||
168 | + if (vsock->guest_cid) | ||
169 | + hash_del_rcu(&vsock->hash); | ||
170 | spin_unlock_bh(&vhost_vsock_lock); | ||
171 | |||
172 | + /* Wait for other CPUs to finish using vsock */ | ||
173 | + synchronize_rcu(); | ||
174 | + | ||
175 | /* Iterating over all connections for all CIDs to find orphans is | ||
176 | * inefficient. Room for improvement here. */ | ||
177 | vsock_for_each_connected_socket(vhost_vsock_reset_orphans); | ||
178 | @@ -618,12 +622,17 @@ static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid) | ||
179 | |||
180 | /* Refuse if CID is already in use */ | ||
181 | spin_lock_bh(&vhost_vsock_lock); | ||
182 | - other = __vhost_vsock_get(guest_cid); | ||
183 | + other = vhost_vsock_get(guest_cid); | ||
184 | if (other && other != vsock) { | ||
185 | spin_unlock_bh(&vhost_vsock_lock); | ||
186 | return -EADDRINUSE; | ||
187 | } | ||
188 | + | ||
189 | + if (vsock->guest_cid) | ||
190 | + hash_del_rcu(&vsock->hash); | ||
191 | + | ||
192 | vsock->guest_cid = guest_cid; | ||
193 | + hash_add_rcu(vhost_vsock_hash, &vsock->hash, guest_cid); | ||
194 | spin_unlock_bh(&vhost_vsock_lock); | ||
195 | |||
196 | return 0; | ||
197 | -- | ||
198 | 2.19.2 | ||
199 | |||