Skip to content

Commit 0deab08

Browse files
stefano-garzarelladavem330
authored andcommitted
vsock/virtio: use RCU to avoid use-after-free on the_virtio_vsock
Some callbacks used by the upper layers can run while we are in the .remove(). A potential use-after-free can happen, because we free the_virtio_vsock without knowing if the callbacks are over or not. To solve this issue we move the assignment of the_virtio_vsock at the end of .probe(), when we finished all the initialization, and at the beginning of .remove(), before to release resources. For the same reason, we do the same also for the vdev->priv. We use RCU to be sure that all callbacks that use the_virtio_vsock ended before freeing it. This is not required for callbacks that use vdev->priv, because after the vdev->config->del_vqs() we are sure that they are ended and will no longer be invoked. We also take the mutex during the .remove() to avoid that .probe() can run while we are resetting the device. Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1a2d405 commit 0deab08

File tree

1 file changed

+46
-24
lines changed

1 file changed

+46
-24
lines changed

net/vmw_vsock/virtio_transport.c

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,22 @@ struct virtio_vsock {
6565
u32 guest_cid;
6666
};
6767

68-
static struct virtio_vsock *virtio_vsock_get(void)
69-
{
70-
return the_virtio_vsock;
71-
}
72-
7368
static u32 virtio_transport_get_local_cid(void)
7469
{
75-
struct virtio_vsock *vsock = virtio_vsock_get();
70+
struct virtio_vsock *vsock;
71+
u32 ret;
7672

77-
if (!vsock)
78-
return VMADDR_CID_ANY;
73+
rcu_read_lock();
74+
vsock = rcu_dereference(the_virtio_vsock);
75+
if (!vsock) {
76+
ret = VMADDR_CID_ANY;
77+
goto out_rcu;
78+
}
7979

80-
return vsock->guest_cid;
80+
ret = vsock->guest_cid;
81+
out_rcu:
82+
rcu_read_unlock();
83+
return ret;
8184
}
8285

8386
static void virtio_transport_loopback_work(struct work_struct *work)
@@ -197,14 +200,18 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt)
197200
struct virtio_vsock *vsock;
198201
int len = pkt->len;
199202

200-
vsock = virtio_vsock_get();
203+
rcu_read_lock();
204+
vsock = rcu_dereference(the_virtio_vsock);
201205
if (!vsock) {
202206
virtio_transport_free_pkt(pkt);
203-
return -ENODEV;
207+
len = -ENODEV;
208+
goto out_rcu;
204209
}
205210

206-
if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid)
207-
return virtio_transport_send_pkt_loopback(vsock, pkt);
211+
if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) {
212+
len = virtio_transport_send_pkt_loopback(vsock, pkt);
213+
goto out_rcu;
214+
}
208215

209216
if (pkt->reply)
210217
atomic_inc(&vsock->queued_replies);
@@ -214,6 +221,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt)
214221
spin_unlock_bh(&vsock->send_pkt_list_lock);
215222

216223
queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work);
224+
225+
out_rcu:
226+
rcu_read_unlock();
217227
return len;
218228
}
219229

@@ -222,12 +232,14 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk)
222232
{
223233
struct virtio_vsock *vsock;
224234
struct virtio_vsock_pkt *pkt, *n;
225-
int cnt = 0;
235+
int cnt = 0, ret;
226236
LIST_HEAD(freeme);
227237

228-
vsock = virtio_vsock_get();
238+
rcu_read_lock();
239+
vsock = rcu_dereference(the_virtio_vsock);
229240
if (!vsock) {
230-
return -ENODEV;
241+
ret = -ENODEV;
242+
goto out_rcu;
231243
}
232244

233245
spin_lock_bh(&vsock->send_pkt_list_lock);
@@ -255,7 +267,11 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk)
255267
queue_work(virtio_vsock_workqueue, &vsock->rx_work);
256268
}
257269

258-
return 0;
270+
ret = 0;
271+
272+
out_rcu:
273+
rcu_read_unlock();
274+
return ret;
259275
}
260276

261277
static void virtio_vsock_rx_fill(struct virtio_vsock *vsock)
@@ -565,7 +581,8 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
565581
return ret;
566582

567583
/* Only one virtio-vsock device per guest is supported */
568-
if (the_virtio_vsock) {
584+
if (rcu_dereference_protected(the_virtio_vsock,
585+
lockdep_is_held(&the_virtio_vsock_mutex))) {
569586
ret = -EBUSY;
570587
goto out;
571588
}
@@ -590,8 +607,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
590607
vsock->rx_buf_max_nr = 0;
591608
atomic_set(&vsock->queued_replies, 0);
592609

593-
vdev->priv = vsock;
594-
the_virtio_vsock = vsock;
595610
mutex_init(&vsock->tx_lock);
596611
mutex_init(&vsock->rx_lock);
597612
mutex_init(&vsock->event_lock);
@@ -613,6 +628,9 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
613628
virtio_vsock_event_fill(vsock);
614629
mutex_unlock(&vsock->event_lock);
615630

631+
vdev->priv = vsock;
632+
rcu_assign_pointer(the_virtio_vsock, vsock);
633+
616634
mutex_unlock(&the_virtio_vsock_mutex);
617635
return 0;
618636

@@ -627,6 +645,12 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
627645
struct virtio_vsock *vsock = vdev->priv;
628646
struct virtio_vsock_pkt *pkt;
629647

648+
mutex_lock(&the_virtio_vsock_mutex);
649+
650+
vdev->priv = NULL;
651+
rcu_assign_pointer(the_virtio_vsock, NULL);
652+
synchronize_rcu();
653+
630654
flush_work(&vsock->loopback_work);
631655
flush_work(&vsock->rx_work);
632656
flush_work(&vsock->tx_work);
@@ -666,12 +690,10 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
666690
}
667691
spin_unlock_bh(&vsock->loopback_list_lock);
668692

669-
mutex_lock(&the_virtio_vsock_mutex);
670-
the_virtio_vsock = NULL;
671-
mutex_unlock(&the_virtio_vsock_mutex);
672-
673693
vdev->config->del_vqs(vdev);
674694

695+
mutex_unlock(&the_virtio_vsock_mutex);
696+
675697
kfree(vsock);
676698
}
677699

0 commit comments

Comments
 (0)