From: Sasha Levin Date: Fri, 2 Oct 2020 23:18:37 +0000 (-0400) Subject: Fixes for 4.14 X-Git-Tag: v4.19.150~51 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3a0b136f218179fc49e7e5074e183ecbbc531470;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 4.14 Signed-off-by: Sasha Levin --- diff --git a/queue-4.14/net-virtio_vsock-enhance-connection-semantics.patch b/queue-4.14/net-virtio_vsock-enhance-connection-semantics.patch new file mode 100644 index 00000000000..c97afab7ca8 --- /dev/null +++ b/queue-4.14/net-virtio_vsock-enhance-connection-semantics.patch @@ -0,0 +1,60 @@ +From 4d6a0b0f1cc172e19c3e513ac539543edc86ecd5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Feb 2020 12:48:01 +0100 +Subject: net: virtio_vsock: Enhance connection semantics + +From: Sebastien Boeuf + +[ Upstream commit df12eb6d6cd920ab2f0e0a43cd6e1c23a05cea91 ] + +Whenever the vsock backend on the host sends a packet through the RX +queue, it expects an answer on the TX queue. Unfortunately, there is one +case where the host side will hang waiting for the answer and might +effectively never recover if no timeout mechanism was implemented. + +This issue happens when the guest side starts binding to the socket, +which insert a new bound socket into the list of already bound sockets. +At this time, we expect the guest to also start listening, which will +trigger the sk_state to move from TCP_CLOSE to TCP_LISTEN. The problem +occurs if the host side queued a RX packet and triggered an interrupt +right between the end of the binding process and the beginning of the +listening process. In this specific case, the function processing the +packet virtio_transport_recv_pkt() will find a bound socket, which means +it will hit the switch statement checking for the sk_state, but the +state won't be changed into TCP_LISTEN yet, which leads the code to pick +the default statement. This default statement will only free the buffer, +while it should also respond to the host side, by sending a packet on +its TX queue. + +In order to simply fix this unfortunate chain of events, it is important +that in case the default statement is entered, and because at this stage +we know the host side is waiting for an answer, we must send back a +packet containing the operation VIRTIO_VSOCK_OP_RST. + +One could say that a proper timeout mechanism on the host side will be +enough to avoid the backend to hang. But the point of this patch is to +ensure the normal use case will be provided with proper responsiveness +when it comes to establishing the connection. + +Signed-off-by: Sebastien Boeuf +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/vmw_vsock/virtio_transport_common.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c +index dc4fb4aa76550..8e4c13cc61ba8 100644 +--- a/net/vmw_vsock/virtio_transport_common.c ++++ b/net/vmw_vsock/virtio_transport_common.c +@@ -1056,6 +1056,7 @@ void virtio_transport_recv_pkt(struct virtio_transport *t, + virtio_transport_free_pkt(pkt); + break; + default: ++ (void)virtio_transport_reset_no_sock(t, pkt); + virtio_transport_free_pkt(pkt); + break; + } +-- +2.25.1 + diff --git a/queue-4.14/series b/queue-4.14/series new file mode 100644 index 00000000000..6727896ec49 --- /dev/null +++ b/queue-4.14/series @@ -0,0 +1,4 @@ +vsock-virtio-use-rcu-to-avoid-use-after-free-on-the_.patch +vsock-virtio-stop-workers-during-the-.remove.patch +vsock-virtio-add-transport-parameter-to-the-virtio_t.patch +net-virtio_vsock-enhance-connection-semantics.patch diff --git a/queue-4.14/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch b/queue-4.14/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch new file mode 100644 index 00000000000..04fec504d89 --- /dev/null +++ b/queue-4.14/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch @@ -0,0 +1,409 @@ +From 4a1783ee9da06a09c515f776d4b011b813f17396 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 Nov 2019 10:57:40 +0100 +Subject: vsock/virtio: add transport parameter to the + virtio_transport_reset_no_sock() + +From: Stefano Garzarella + +[ Upstream commit 4c7246dc45e2706770d5233f7ce1597a07e069ba ] + +We are going to add 'struct vsock_sock *' parameter to +virtio_transport_get_ops(). + +In some cases, like in the virtio_transport_reset_no_sock(), +we don't have any socket assigned to the packet received, +so we can't use the virtio_transport_get_ops(). + +In order to allow virtio_transport_reset_no_sock() to use the +'.send_pkt' callback from the 'vhost_transport' or 'virtio_transport', +we add the 'struct virtio_transport *' to it and to its caller: +virtio_transport_recv_pkt(). + +We moved the 'vhost_transport' and 'virtio_transport' definition, +to pass their address to the virtio_transport_recv_pkt(). + +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Stefano Garzarella +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/vhost/vsock.c | 94 +++++++------- + include/linux/virtio_vsock.h | 3 +- + net/vmw_vsock/virtio_transport.c | 160 ++++++++++++------------ + net/vmw_vsock/virtio_transport_common.c | 12 +- + 4 files changed, 135 insertions(+), 134 deletions(-) + +diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c +index 3f2f34ebf51f5..27f3b471c95bf 100644 +--- a/drivers/vhost/vsock.c ++++ b/drivers/vhost/vsock.c +@@ -383,6 +383,52 @@ static bool vhost_vsock_more_replies(struct vhost_vsock *vsock) + return val < vq->num; + } + ++static struct virtio_transport vhost_transport = { ++ .transport = { ++ .get_local_cid = vhost_transport_get_local_cid, ++ ++ .init = virtio_transport_do_socket_init, ++ .destruct = virtio_transport_destruct, ++ .release = virtio_transport_release, ++ .connect = virtio_transport_connect, ++ .shutdown = virtio_transport_shutdown, ++ .cancel_pkt = vhost_transport_cancel_pkt, ++ ++ .dgram_enqueue = virtio_transport_dgram_enqueue, ++ .dgram_dequeue = virtio_transport_dgram_dequeue, ++ .dgram_bind = virtio_transport_dgram_bind, ++ .dgram_allow = virtio_transport_dgram_allow, ++ ++ .stream_enqueue = virtio_transport_stream_enqueue, ++ .stream_dequeue = virtio_transport_stream_dequeue, ++ .stream_has_data = virtio_transport_stream_has_data, ++ .stream_has_space = virtio_transport_stream_has_space, ++ .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, ++ .stream_is_active = virtio_transport_stream_is_active, ++ .stream_allow = virtio_transport_stream_allow, ++ ++ .notify_poll_in = virtio_transport_notify_poll_in, ++ .notify_poll_out = virtio_transport_notify_poll_out, ++ .notify_recv_init = virtio_transport_notify_recv_init, ++ .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, ++ .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, ++ .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, ++ .notify_send_init = virtio_transport_notify_send_init, ++ .notify_send_pre_block = virtio_transport_notify_send_pre_block, ++ .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, ++ .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, ++ ++ .set_buffer_size = virtio_transport_set_buffer_size, ++ .set_min_buffer_size = virtio_transport_set_min_buffer_size, ++ .set_max_buffer_size = virtio_transport_set_max_buffer_size, ++ .get_buffer_size = virtio_transport_get_buffer_size, ++ .get_min_buffer_size = virtio_transport_get_min_buffer_size, ++ .get_max_buffer_size = virtio_transport_get_max_buffer_size, ++ }, ++ ++ .send_pkt = vhost_transport_send_pkt, ++}; ++ + static void vhost_vsock_handle_tx_kick(struct vhost_work *work) + { + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, +@@ -439,7 +485,7 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work) + if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid && + le64_to_cpu(pkt->hdr.dst_cid) == + vhost_transport_get_local_cid()) +- virtio_transport_recv_pkt(pkt); ++ virtio_transport_recv_pkt(&vhost_transport, pkt); + else + virtio_transport_free_pkt(pkt); + +@@ -780,52 +826,6 @@ static struct miscdevice vhost_vsock_misc = { + .fops = &vhost_vsock_fops, + }; + +-static struct virtio_transport vhost_transport = { +- .transport = { +- .get_local_cid = vhost_transport_get_local_cid, +- +- .init = virtio_transport_do_socket_init, +- .destruct = virtio_transport_destruct, +- .release = virtio_transport_release, +- .connect = virtio_transport_connect, +- .shutdown = virtio_transport_shutdown, +- .cancel_pkt = vhost_transport_cancel_pkt, +- +- .dgram_enqueue = virtio_transport_dgram_enqueue, +- .dgram_dequeue = virtio_transport_dgram_dequeue, +- .dgram_bind = virtio_transport_dgram_bind, +- .dgram_allow = virtio_transport_dgram_allow, +- +- .stream_enqueue = virtio_transport_stream_enqueue, +- .stream_dequeue = virtio_transport_stream_dequeue, +- .stream_has_data = virtio_transport_stream_has_data, +- .stream_has_space = virtio_transport_stream_has_space, +- .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, +- .stream_is_active = virtio_transport_stream_is_active, +- .stream_allow = virtio_transport_stream_allow, +- +- .notify_poll_in = virtio_transport_notify_poll_in, +- .notify_poll_out = virtio_transport_notify_poll_out, +- .notify_recv_init = virtio_transport_notify_recv_init, +- .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, +- .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, +- .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, +- .notify_send_init = virtio_transport_notify_send_init, +- .notify_send_pre_block = virtio_transport_notify_send_pre_block, +- .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, +- .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, +- +- .set_buffer_size = virtio_transport_set_buffer_size, +- .set_min_buffer_size = virtio_transport_set_min_buffer_size, +- .set_max_buffer_size = virtio_transport_set_max_buffer_size, +- .get_buffer_size = virtio_transport_get_buffer_size, +- .get_min_buffer_size = virtio_transport_get_min_buffer_size, +- .get_max_buffer_size = virtio_transport_get_max_buffer_size, +- }, +- +- .send_pkt = vhost_transport_send_pkt, +-}; +- + static int __init vhost_vsock_init(void) + { + int ret; +diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h +index e223e2632eddd..8b8d13f01caee 100644 +--- a/include/linux/virtio_vsock.h ++++ b/include/linux/virtio_vsock.h +@@ -149,7 +149,8 @@ virtio_transport_dgram_enqueue(struct vsock_sock *vsk, + + void virtio_transport_destruct(struct vsock_sock *vsk); + +-void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt); ++void virtio_transport_recv_pkt(struct virtio_transport *t, ++ struct virtio_vsock_pkt *pkt); + void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt); + void virtio_transport_inc_tx_pkt(struct virtio_vsock_sock *vvs, struct virtio_vsock_pkt *pkt); + u32 virtio_transport_get_credit(struct virtio_vsock_sock *vvs, u32 wanted); +diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c +index 4bc217ef56945..cc70d651d13e0 100644 +--- a/net/vmw_vsock/virtio_transport.c ++++ b/net/vmw_vsock/virtio_transport.c +@@ -87,33 +87,6 @@ static u32 virtio_transport_get_local_cid(void) + return ret; + } + +-static void virtio_transport_loopback_work(struct work_struct *work) +-{ +- struct virtio_vsock *vsock = +- container_of(work, struct virtio_vsock, loopback_work); +- LIST_HEAD(pkts); +- +- spin_lock_bh(&vsock->loopback_list_lock); +- list_splice_init(&vsock->loopback_list, &pkts); +- spin_unlock_bh(&vsock->loopback_list_lock); +- +- mutex_lock(&vsock->rx_lock); +- +- if (!vsock->rx_run) +- goto out; +- +- while (!list_empty(&pkts)) { +- struct virtio_vsock_pkt *pkt; +- +- pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list); +- list_del_init(&pkt->list); +- +- virtio_transport_recv_pkt(pkt); +- } +-out: +- mutex_unlock(&vsock->rx_lock); +-} +- + static int virtio_transport_send_pkt_loopback(struct virtio_vsock *vsock, + struct virtio_vsock_pkt *pkt) + { +@@ -370,59 +343,6 @@ static bool virtio_transport_more_replies(struct virtio_vsock *vsock) + return val < virtqueue_get_vring_size(vq); + } + +-static void virtio_transport_rx_work(struct work_struct *work) +-{ +- struct virtio_vsock *vsock = +- container_of(work, struct virtio_vsock, rx_work); +- struct virtqueue *vq; +- +- vq = vsock->vqs[VSOCK_VQ_RX]; +- +- mutex_lock(&vsock->rx_lock); +- +- if (!vsock->rx_run) +- goto out; +- +- do { +- virtqueue_disable_cb(vq); +- for (;;) { +- struct virtio_vsock_pkt *pkt; +- unsigned int len; +- +- if (!virtio_transport_more_replies(vsock)) { +- /* Stop rx until the device processes already +- * pending replies. Leave rx virtqueue +- * callbacks disabled. +- */ +- goto out; +- } +- +- pkt = virtqueue_get_buf(vq, &len); +- if (!pkt) { +- break; +- } +- +- vsock->rx_buf_nr--; +- +- /* Drop short/long packets */ +- if (unlikely(len < sizeof(pkt->hdr) || +- len > sizeof(pkt->hdr) + pkt->len)) { +- virtio_transport_free_pkt(pkt); +- continue; +- } +- +- pkt->len = len - sizeof(pkt->hdr); +- virtio_transport_deliver_tap_pkt(pkt); +- virtio_transport_recv_pkt(pkt); +- } +- } while (!virtqueue_enable_cb(vq)); +- +-out: +- if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) +- virtio_vsock_rx_fill(vsock); +- mutex_unlock(&vsock->rx_lock); +-} +- + /* event_lock must be held */ + static int virtio_vsock_event_fill_one(struct virtio_vsock *vsock, + struct virtio_vsock_event *event) +@@ -586,6 +506,86 @@ static struct virtio_transport virtio_transport = { + .send_pkt = virtio_transport_send_pkt, + }; + ++static void virtio_transport_loopback_work(struct work_struct *work) ++{ ++ struct virtio_vsock *vsock = ++ container_of(work, struct virtio_vsock, loopback_work); ++ LIST_HEAD(pkts); ++ ++ spin_lock_bh(&vsock->loopback_list_lock); ++ list_splice_init(&vsock->loopback_list, &pkts); ++ spin_unlock_bh(&vsock->loopback_list_lock); ++ ++ mutex_lock(&vsock->rx_lock); ++ ++ if (!vsock->rx_run) ++ goto out; ++ ++ while (!list_empty(&pkts)) { ++ struct virtio_vsock_pkt *pkt; ++ ++ pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list); ++ list_del_init(&pkt->list); ++ ++ virtio_transport_recv_pkt(&virtio_transport, pkt); ++ } ++out: ++ mutex_unlock(&vsock->rx_lock); ++} ++ ++static void virtio_transport_rx_work(struct work_struct *work) ++{ ++ struct virtio_vsock *vsock = ++ container_of(work, struct virtio_vsock, rx_work); ++ struct virtqueue *vq; ++ ++ vq = vsock->vqs[VSOCK_VQ_RX]; ++ ++ mutex_lock(&vsock->rx_lock); ++ ++ if (!vsock->rx_run) ++ goto out; ++ ++ do { ++ virtqueue_disable_cb(vq); ++ for (;;) { ++ struct virtio_vsock_pkt *pkt; ++ unsigned int len; ++ ++ if (!virtio_transport_more_replies(vsock)) { ++ /* Stop rx until the device processes already ++ * pending replies. Leave rx virtqueue ++ * callbacks disabled. ++ */ ++ goto out; ++ } ++ ++ pkt = virtqueue_get_buf(vq, &len); ++ if (!pkt) { ++ break; ++ } ++ ++ vsock->rx_buf_nr--; ++ ++ /* Drop short/long packets */ ++ if (unlikely(len < sizeof(pkt->hdr) || ++ len > sizeof(pkt->hdr) + pkt->len)) { ++ virtio_transport_free_pkt(pkt); ++ continue; ++ } ++ ++ pkt->len = len - sizeof(pkt->hdr); ++ virtio_transport_deliver_tap_pkt(pkt); ++ virtio_transport_recv_pkt(&virtio_transport, pkt); ++ } ++ } while (!virtqueue_enable_cb(vq)); ++ ++out: ++ if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) ++ virtio_vsock_rx_fill(vsock); ++ mutex_unlock(&vsock->rx_lock); ++} ++ + static int virtio_vsock_probe(struct virtio_device *vdev) + { + vq_callback_t *callbacks[] = { +diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c +index d20f43057323e..dc4fb4aa76550 100644 +--- a/net/vmw_vsock/virtio_transport_common.c ++++ b/net/vmw_vsock/virtio_transport_common.c +@@ -669,9 +669,9 @@ static int virtio_transport_reset(struct vsock_sock *vsk, + /* Normally packets are associated with a socket. There may be no socket if an + * attempt was made to connect to a socket that does not exist. + */ +-static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt) ++static int virtio_transport_reset_no_sock(const struct virtio_transport *t, ++ struct virtio_vsock_pkt *pkt) + { +- const struct virtio_transport *t; + struct virtio_vsock_pkt *reply; + struct virtio_vsock_pkt_info info = { + .op = VIRTIO_VSOCK_OP_RST, +@@ -691,7 +691,6 @@ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt) + if (!reply) + return -ENOMEM; + +- t = virtio_transport_get_ops(); + if (!t) { + virtio_transport_free_pkt(reply); + return -ENOTCONN; +@@ -989,7 +988,8 @@ static bool virtio_transport_space_update(struct sock *sk, + /* We are under the virtio-vsock's vsock->rx_lock or vhost-vsock's vq->mutex + * lock. + */ +-void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) ++void virtio_transport_recv_pkt(struct virtio_transport *t, ++ struct virtio_vsock_pkt *pkt) + { + struct sockaddr_vm src, dst; + struct vsock_sock *vsk; +@@ -1011,7 +1011,7 @@ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) + le32_to_cpu(pkt->hdr.fwd_cnt)); + + if (le16_to_cpu(pkt->hdr.type) != VIRTIO_VSOCK_TYPE_STREAM) { +- (void)virtio_transport_reset_no_sock(pkt); ++ (void)virtio_transport_reset_no_sock(t, pkt); + goto free_pkt; + } + +@@ -1022,7 +1022,7 @@ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) + if (!sk) { + sk = vsock_find_bound_socket(&dst); + if (!sk) { +- (void)virtio_transport_reset_no_sock(pkt); ++ (void)virtio_transport_reset_no_sock(t, pkt); + goto free_pkt; + } + } +-- +2.25.1 + diff --git a/queue-4.14/vsock-virtio-stop-workers-during-the-.remove.patch b/queue-4.14/vsock-virtio-stop-workers-during-the-.remove.patch new file mode 100644 index 00000000000..ade9a110e16 --- /dev/null +++ b/queue-4.14/vsock-virtio-stop-workers-during-the-.remove.patch @@ -0,0 +1,194 @@ +From 26c3657783d72d91c8eb81863a0b72a3f324f384 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jul 2019 13:04:53 +0200 +Subject: vsock/virtio: stop workers during the .remove() + +From: Stefano Garzarella + +[ Upstream commit 17dd1367389cfe7f150790c83247b68e0c19d106 ] + +Before to call vdev->config->reset(vdev) we need to be sure that +no one is accessing the device, for this reason, we add new variables +in the struct virtio_vsock to stop the workers during the .remove(). + +This patch also add few comments before vdev->config->reset(vdev) +and vdev->config->del_vqs(vdev). + +Suggested-by: Stefan Hajnoczi +Suggested-by: Michael S. Tsirkin +Signed-off-by: Stefano Garzarella +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/vmw_vsock/virtio_transport.c | 51 +++++++++++++++++++++++++++++++- + 1 file changed, 50 insertions(+), 1 deletion(-) + +diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c +index 68186419c445f..4bc217ef56945 100644 +--- a/net/vmw_vsock/virtio_transport.c ++++ b/net/vmw_vsock/virtio_transport.c +@@ -39,6 +39,7 @@ struct virtio_vsock { + * must be accessed with tx_lock held. + */ + struct mutex tx_lock; ++ bool tx_run; + + struct work_struct send_pkt_work; + spinlock_t send_pkt_list_lock; +@@ -54,6 +55,7 @@ struct virtio_vsock { + * must be accessed with rx_lock held. + */ + struct mutex rx_lock; ++ bool rx_run; + int rx_buf_nr; + int rx_buf_max_nr; + +@@ -61,6 +63,7 @@ struct virtio_vsock { + * vqs[VSOCK_VQ_EVENT] must be accessed with event_lock held. + */ + struct mutex event_lock; ++ bool event_run; + struct virtio_vsock_event event_list[8]; + + u32 guest_cid; +@@ -95,6 +98,10 @@ static void virtio_transport_loopback_work(struct work_struct *work) + spin_unlock_bh(&vsock->loopback_list_lock); + + mutex_lock(&vsock->rx_lock); ++ ++ if (!vsock->rx_run) ++ goto out; ++ + while (!list_empty(&pkts)) { + struct virtio_vsock_pkt *pkt; + +@@ -103,6 +110,7 @@ static void virtio_transport_loopback_work(struct work_struct *work) + + virtio_transport_recv_pkt(pkt); + } ++out: + mutex_unlock(&vsock->rx_lock); + } + +@@ -131,6 +139,9 @@ virtio_transport_send_pkt_work(struct work_struct *work) + + mutex_lock(&vsock->tx_lock); + ++ if (!vsock->tx_run) ++ goto out; ++ + vq = vsock->vqs[VSOCK_VQ_TX]; + + for (;;) { +@@ -189,6 +200,7 @@ virtio_transport_send_pkt_work(struct work_struct *work) + if (added) + virtqueue_kick(vq); + ++out: + mutex_unlock(&vsock->tx_lock); + + if (restart_rx) +@@ -324,6 +336,10 @@ static void virtio_transport_tx_work(struct work_struct *work) + + vq = vsock->vqs[VSOCK_VQ_TX]; + mutex_lock(&vsock->tx_lock); ++ ++ if (!vsock->tx_run) ++ goto out; ++ + do { + struct virtio_vsock_pkt *pkt; + unsigned int len; +@@ -334,6 +350,8 @@ static void virtio_transport_tx_work(struct work_struct *work) + added = true; + } + } while (!virtqueue_enable_cb(vq)); ++ ++out: + mutex_unlock(&vsock->tx_lock); + + if (added) +@@ -362,6 +380,9 @@ static void virtio_transport_rx_work(struct work_struct *work) + + mutex_lock(&vsock->rx_lock); + ++ if (!vsock->rx_run) ++ goto out; ++ + do { + virtqueue_disable_cb(vq); + for (;;) { +@@ -471,6 +492,9 @@ static void virtio_transport_event_work(struct work_struct *work) + + mutex_lock(&vsock->event_lock); + ++ if (!vsock->event_run) ++ goto out; ++ + do { + struct virtio_vsock_event *event; + unsigned int len; +@@ -485,7 +509,7 @@ static void virtio_transport_event_work(struct work_struct *work) + } while (!virtqueue_enable_cb(vq)); + + virtqueue_kick(vsock->vqs[VSOCK_VQ_EVENT]); +- ++out: + mutex_unlock(&vsock->event_lock); + } + +@@ -621,12 +645,18 @@ static int virtio_vsock_probe(struct virtio_device *vdev) + INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work); + INIT_WORK(&vsock->loopback_work, virtio_transport_loopback_work); + ++ mutex_lock(&vsock->tx_lock); ++ vsock->tx_run = true; ++ mutex_unlock(&vsock->tx_lock); ++ + mutex_lock(&vsock->rx_lock); + virtio_vsock_rx_fill(vsock); ++ vsock->rx_run = true; + mutex_unlock(&vsock->rx_lock); + + mutex_lock(&vsock->event_lock); + virtio_vsock_event_fill(vsock); ++ vsock->event_run = true; + mutex_unlock(&vsock->event_lock); + + vdev->priv = vsock; +@@ -661,6 +691,24 @@ static void virtio_vsock_remove(struct virtio_device *vdev) + /* Reset all connected sockets when the device disappear */ + vsock_for_each_connected_socket(virtio_vsock_reset_sock); + ++ /* Stop all work handlers to make sure no one is accessing the device, ++ * so we can safely call vdev->config->reset(). ++ */ ++ mutex_lock(&vsock->rx_lock); ++ vsock->rx_run = false; ++ mutex_unlock(&vsock->rx_lock); ++ ++ mutex_lock(&vsock->tx_lock); ++ vsock->tx_run = false; ++ mutex_unlock(&vsock->tx_lock); ++ ++ mutex_lock(&vsock->event_lock); ++ vsock->event_run = false; ++ mutex_unlock(&vsock->event_lock); ++ ++ /* Flush all device writes and interrupts, device will not use any ++ * more buffers. ++ */ + vdev->config->reset(vdev); + + mutex_lock(&vsock->rx_lock); +@@ -691,6 +739,7 @@ static void virtio_vsock_remove(struct virtio_device *vdev) + } + spin_unlock_bh(&vsock->loopback_list_lock); + ++ /* Delete virtqueues and flush outstanding callbacks if any */ + vdev->config->del_vqs(vdev); + + mutex_unlock(&the_virtio_vsock_mutex); +-- +2.25.1 + diff --git a/queue-4.14/vsock-virtio-use-rcu-to-avoid-use-after-free-on-the_.patch b/queue-4.14/vsock-virtio-use-rcu-to-avoid-use-after-free-on-the_.patch new file mode 100644 index 00000000000..5475c3ee47d --- /dev/null +++ b/queue-4.14/vsock-virtio-use-rcu-to-avoid-use-after-free-on-the_.patch @@ -0,0 +1,193 @@ +From 424b558df3e122eaef4f2753c0c5ad574b5e9e5c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jul 2019 13:04:52 +0200 +Subject: vsock/virtio: use RCU to avoid use-after-free on the_virtio_vsock + +From: Stefano Garzarella + +[ Upstream commit 9c7a5582f5d720dc35cfcc42ccaded69f0642e4a ] + +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 +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/vmw_vsock/virtio_transport.c | 70 +++++++++++++++++++++----------- + 1 file changed, 46 insertions(+), 24 deletions(-) + +diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c +index 96ab344f17bbc..68186419c445f 100644 +--- a/net/vmw_vsock/virtio_transport.c ++++ b/net/vmw_vsock/virtio_transport.c +@@ -66,19 +66,22 @@ struct virtio_vsock { + u32 guest_cid; + }; + +-static struct virtio_vsock *virtio_vsock_get(void) +-{ +- return the_virtio_vsock; +-} +- + static u32 virtio_transport_get_local_cid(void) + { +- struct virtio_vsock *vsock = virtio_vsock_get(); ++ struct virtio_vsock *vsock; ++ u32 ret; + +- if (!vsock) +- return VMADDR_CID_ANY; ++ rcu_read_lock(); ++ vsock = rcu_dereference(the_virtio_vsock); ++ if (!vsock) { ++ ret = VMADDR_CID_ANY; ++ goto out_rcu; ++ } + +- return vsock->guest_cid; ++ ret = vsock->guest_cid; ++out_rcu: ++ rcu_read_unlock(); ++ return ret; + } + + static void virtio_transport_loopback_work(struct work_struct *work) +@@ -198,14 +201,18 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) + struct virtio_vsock *vsock; + int len = pkt->len; + +- vsock = virtio_vsock_get(); ++ rcu_read_lock(); ++ vsock = rcu_dereference(the_virtio_vsock); + if (!vsock) { + virtio_transport_free_pkt(pkt); +- return -ENODEV; ++ len = -ENODEV; ++ goto out_rcu; + } + +- if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) +- return virtio_transport_send_pkt_loopback(vsock, pkt); ++ if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) { ++ len = virtio_transport_send_pkt_loopback(vsock, pkt); ++ goto out_rcu; ++ } + + if (pkt->reply) + atomic_inc(&vsock->queued_replies); +@@ -215,6 +222,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) + spin_unlock_bh(&vsock->send_pkt_list_lock); + + queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); ++ ++out_rcu: ++ rcu_read_unlock(); + return len; + } + +@@ -223,12 +233,14 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk) + { + struct virtio_vsock *vsock; + struct virtio_vsock_pkt *pkt, *n; +- int cnt = 0; ++ int cnt = 0, ret; + LIST_HEAD(freeme); + +- vsock = virtio_vsock_get(); ++ rcu_read_lock(); ++ vsock = rcu_dereference(the_virtio_vsock); + if (!vsock) { +- return -ENODEV; ++ ret = -ENODEV; ++ goto out_rcu; + } + + spin_lock_bh(&vsock->send_pkt_list_lock); +@@ -256,7 +268,11 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk) + queue_work(virtio_vsock_workqueue, &vsock->rx_work); + } + +- return 0; ++ ret = 0; ++ ++out_rcu: ++ rcu_read_unlock(); ++ return ret; + } + + static void virtio_vsock_rx_fill(struct virtio_vsock *vsock) +@@ -566,7 +582,8 @@ static int virtio_vsock_probe(struct virtio_device *vdev) + return ret; + + /* Only one virtio-vsock device per guest is supported */ +- if (the_virtio_vsock) { ++ if (rcu_dereference_protected(the_virtio_vsock, ++ lockdep_is_held(&the_virtio_vsock_mutex))) { + ret = -EBUSY; + goto out; + } +@@ -591,8 +608,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev) + vsock->rx_buf_max_nr = 0; + atomic_set(&vsock->queued_replies, 0); + +- vdev->priv = vsock; +- the_virtio_vsock = vsock; + mutex_init(&vsock->tx_lock); + mutex_init(&vsock->rx_lock); + mutex_init(&vsock->event_lock); +@@ -614,6 +629,9 @@ static int virtio_vsock_probe(struct virtio_device *vdev) + virtio_vsock_event_fill(vsock); + mutex_unlock(&vsock->event_lock); + ++ vdev->priv = vsock; ++ rcu_assign_pointer(the_virtio_vsock, vsock); ++ + mutex_unlock(&the_virtio_vsock_mutex); + return 0; + +@@ -628,6 +646,12 @@ static void virtio_vsock_remove(struct virtio_device *vdev) + struct virtio_vsock *vsock = vdev->priv; + struct virtio_vsock_pkt *pkt; + ++ mutex_lock(&the_virtio_vsock_mutex); ++ ++ vdev->priv = NULL; ++ rcu_assign_pointer(the_virtio_vsock, NULL); ++ synchronize_rcu(); ++ + flush_work(&vsock->loopback_work); + flush_work(&vsock->rx_work); + flush_work(&vsock->tx_work); +@@ -667,12 +691,10 @@ static void virtio_vsock_remove(struct virtio_device *vdev) + } + spin_unlock_bh(&vsock->loopback_list_lock); + +- mutex_lock(&the_virtio_vsock_mutex); +- the_virtio_vsock = NULL; +- mutex_unlock(&the_virtio_vsock_mutex); +- + vdev->config->del_vqs(vdev); + ++ mutex_unlock(&the_virtio_vsock_mutex); ++ + kfree(vsock); + } + +-- +2.25.1 +