Skip to content

Commit 834e772

Browse files
stefanhaRHmstsirkin
authored andcommitted
vhost/vsock: fix use-after-free in network stack callers
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". 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 <stefanha@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Jason Wang <jasowang@redhat.com>
1 parent 78b1a52 commit 834e772

File tree

1 file changed

+33
-24
lines changed

1 file changed

+33
-24
lines changed

drivers/vhost/vsock.c

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <net/sock.h>
1616
#include <linux/virtio_vsock.h>
1717
#include <linux/vhost.h>
18+
#include <linux/hashtable.h>
1819

1920
#include <net/af_vsock.h>
2021
#include "vhost.h"
@@ -27,14 +28,14 @@ enum {
2728

2829
/* Used to track all the vhost_vsock instances on the system. */
2930
static DEFINE_SPINLOCK(vhost_vsock_lock);
30-
static LIST_HEAD(vhost_vsock_list);
31+
static DEFINE_READ_MOSTLY_HASHTABLE(vhost_vsock_hash, 8);
3132

3233
struct vhost_vsock {
3334
struct vhost_dev dev;
3435
struct vhost_virtqueue vqs[2];
3536

36-
/* Link to global vhost_vsock_list, protected by vhost_vsock_lock */
37-
struct list_head list;
37+
/* Link to global vhost_vsock_hash, writes use vhost_vsock_lock */
38+
struct hlist_node hash;
3839

3940
struct vhost_work send_pkt_work;
4041
spinlock_t send_pkt_list_lock;
@@ -50,11 +51,14 @@ static u32 vhost_transport_get_local_cid(void)
5051
return VHOST_VSOCK_DEFAULT_HOST_CID;
5152
}
5253

53-
static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid)
54+
/* Callers that dereference the return value must hold vhost_vsock_lock or the
55+
* RCU read lock.
56+
*/
57+
static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
5458
{
5559
struct vhost_vsock *vsock;
5660

57-
list_for_each_entry(vsock, &vhost_vsock_list, list) {
61+
hash_for_each_possible_rcu(vhost_vsock_hash, vsock, hash, guest_cid) {
5862
u32 other_cid = vsock->guest_cid;
5963

6064
/* Skip instances that have no CID yet */
@@ -69,17 +73,6 @@ static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid)
6973
return NULL;
7074
}
7175

72-
static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
73-
{
74-
struct vhost_vsock *vsock;
75-
76-
spin_lock_bh(&vhost_vsock_lock);
77-
vsock = __vhost_vsock_get(guest_cid);
78-
spin_unlock_bh(&vhost_vsock_lock);
79-
80-
return vsock;
81-
}
82-
8376
static void
8477
vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
8578
struct vhost_virtqueue *vq)
@@ -210,9 +203,12 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt)
210203
struct vhost_vsock *vsock;
211204
int len = pkt->len;
212205

206+
rcu_read_lock();
207+
213208
/* Find the vhost_vsock according to guest context id */
214209
vsock = vhost_vsock_get(le64_to_cpu(pkt->hdr.dst_cid));
215210
if (!vsock) {
211+
rcu_read_unlock();
216212
virtio_transport_free_pkt(pkt);
217213
return -ENODEV;
218214
}
@@ -225,6 +221,8 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt)
225221
spin_unlock_bh(&vsock->send_pkt_list_lock);
226222

227223
vhost_work_queue(&vsock->dev, &vsock->send_pkt_work);
224+
225+
rcu_read_unlock();
228226
return len;
229227
}
230228

@@ -234,12 +232,15 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk)
234232
struct vhost_vsock *vsock;
235233
struct virtio_vsock_pkt *pkt, *n;
236234
int cnt = 0;
235+
int ret = -ENODEV;
237236
LIST_HEAD(freeme);
238237

238+
rcu_read_lock();
239+
239240
/* Find the vhost_vsock according to guest context id */
240241
vsock = vhost_vsock_get(vsk->remote_addr.svm_cid);
241242
if (!vsock)
242-
return -ENODEV;
243+
goto out;
243244

244245
spin_lock_bh(&vsock->send_pkt_list_lock);
245246
list_for_each_entry_safe(pkt, n, &vsock->send_pkt_list, list) {
@@ -265,7 +266,10 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk)
265266
vhost_poll_queue(&tx_vq->poll);
266267
}
267268

268-
return 0;
269+
ret = 0;
270+
out:
271+
rcu_read_unlock();
272+
return ret;
269273
}
270274

271275
static struct virtio_vsock_pkt *
@@ -533,10 +537,6 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
533537
spin_lock_init(&vsock->send_pkt_list_lock);
534538
INIT_LIST_HEAD(&vsock->send_pkt_list);
535539
vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work);
536-
537-
spin_lock_bh(&vhost_vsock_lock);
538-
list_add_tail(&vsock->list, &vhost_vsock_list);
539-
spin_unlock_bh(&vhost_vsock_lock);
540540
return 0;
541541

542542
out:
@@ -585,9 +585,13 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
585585
struct vhost_vsock *vsock = file->private_data;
586586

587587
spin_lock_bh(&vhost_vsock_lock);
588-
list_del(&vsock->list);
588+
if (vsock->guest_cid)
589+
hash_del_rcu(&vsock->hash);
589590
spin_unlock_bh(&vhost_vsock_lock);
590591

592+
/* Wait for other CPUs to finish using vsock */
593+
synchronize_rcu();
594+
591595
/* Iterating over all connections for all CIDs to find orphans is
592596
* inefficient. Room for improvement here. */
593597
vsock_for_each_connected_socket(vhost_vsock_reset_orphans);
@@ -628,12 +632,17 @@ static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid)
628632

629633
/* Refuse if CID is already in use */
630634
spin_lock_bh(&vhost_vsock_lock);
631-
other = __vhost_vsock_get(guest_cid);
635+
other = vhost_vsock_get(guest_cid);
632636
if (other && other != vsock) {
633637
spin_unlock_bh(&vhost_vsock_lock);
634638
return -EADDRINUSE;
635639
}
640+
641+
if (vsock->guest_cid)
642+
hash_del_rcu(&vsock->hash);
643+
636644
vsock->guest_cid = guest_cid;
645+
hash_add_rcu(vhost_vsock_hash, &vsock->hash, guest_cid);
637646
spin_unlock_bh(&vhost_vsock_lock);
638647

639648
return 0;

0 commit comments

Comments
 (0)