| From 3d107958b1112b8b22b6d6a2b1dd07b52112a9f8 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 20 Apr 2021 13:07:27 +0200 |
| Subject: vsock/virtio: free queued packets when closing socket |
| |
| From: Stefano Garzarella <sgarzare@redhat.com> |
| |
| [ Upstream commit 8432b8114957235f42e070a16118a7f750de9d39 ] |
| |
| As reported by syzbot [1], there is a memory leak while closing the |
| socket. We partially solved this issue with commit ac03046ece2b |
| ("vsock/virtio: free packets during the socket release"), but we |
| forgot to drain the RX queue when the socket is definitely closed by |
| the scheduled work. |
| |
| To avoid future issues, let's use the new virtio_transport_remove_sock() |
| to drain the RX queue before removing the socket from the af_vsock lists |
| calling vsock_remove_sock(). |
| |
| [1] https://syzkaller.appspot.com/bug?extid=24452624fc4c571eedd9 |
| |
| Fixes: ac03046ece2b ("vsock/virtio: free packets during the socket release") |
| Reported-and-tested-by: syzbot+24452624fc4c571eedd9@syzkaller.appspotmail.com |
| Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/vmw_vsock/virtio_transport_common.c | 28 +++++++++++++++++-------- |
| 1 file changed, 19 insertions(+), 9 deletions(-) |
| |
| diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c |
| index e4370b1b7494..902cb6dd710b 100644 |
| --- a/net/vmw_vsock/virtio_transport_common.c |
| +++ b/net/vmw_vsock/virtio_transport_common.c |
| @@ -733,6 +733,23 @@ static int virtio_transport_reset_no_sock(const struct virtio_transport *t, |
| return t->send_pkt(reply); |
| } |
| |
| +/* This function should be called with sk_lock held and SOCK_DONE set */ |
| +static void virtio_transport_remove_sock(struct vsock_sock *vsk) |
| +{ |
| + struct virtio_vsock_sock *vvs = vsk->trans; |
| + struct virtio_vsock_pkt *pkt, *tmp; |
| + |
| + /* We don't need to take rx_lock, as the socket is closing and we are |
| + * removing it. |
| + */ |
| + list_for_each_entry_safe(pkt, tmp, &vvs->rx_queue, list) { |
| + list_del(&pkt->list); |
| + virtio_transport_free_pkt(pkt); |
| + } |
| + |
| + vsock_remove_sock(vsk); |
| +} |
| + |
| static void virtio_transport_wait_close(struct sock *sk, long timeout) |
| { |
| if (timeout) { |
| @@ -765,7 +782,7 @@ static void virtio_transport_do_close(struct vsock_sock *vsk, |
| (!cancel_timeout || cancel_delayed_work(&vsk->close_work))) { |
| vsk->close_work_scheduled = false; |
| |
| - vsock_remove_sock(vsk); |
| + virtio_transport_remove_sock(vsk); |
| |
| /* Release refcnt obtained when we scheduled the timeout */ |
| sock_put(sk); |
| @@ -828,22 +845,15 @@ static bool virtio_transport_close(struct vsock_sock *vsk) |
| |
| void virtio_transport_release(struct vsock_sock *vsk) |
| { |
| - struct virtio_vsock_sock *vvs = vsk->trans; |
| - struct virtio_vsock_pkt *pkt, *tmp; |
| struct sock *sk = &vsk->sk; |
| bool remove_sock = true; |
| |
| if (sk->sk_type == SOCK_STREAM) |
| remove_sock = virtio_transport_close(vsk); |
| |
| - list_for_each_entry_safe(pkt, tmp, &vvs->rx_queue, list) { |
| - list_del(&pkt->list); |
| - virtio_transport_free_pkt(pkt); |
| - } |
| - |
| if (remove_sock) { |
| sock_set_flag(sk, SOCK_DONE); |
| - vsock_remove_sock(vsk); |
| + virtio_transport_remove_sock(vsk); |
| } |
| } |
| EXPORT_SYMBOL_GPL(virtio_transport_release); |
| -- |
| 2.30.2 |
| |