diff mbox series

[net,1/4] virtio/vsock: Fix accept_queue memory leak

Message ID 20241106-vsock-mem-leaks-v1-1-8f4ffc3099e6@rbox.co (mailing list archive)
State New
Headers show
Series virtio/vsock: Fix memory leaks | expand

Commit Message

Michal Luczaj Nov. 6, 2024, 5:51 p.m. UTC
As the final stages of socket destruction may be delayed, it is possible
that virtio_transport_recv_listen() will be called after the accept_queue
has been flushed, but before the SOCK_DONE flag has been set. As a result,
sockets enqueued after the flush would remain unremoved, leading to a
memory leak.

vsock_release
  __vsock_release
    lock
    virtio_transport_release
      virtio_transport_close
        schedule_delayed_work(close_work)
    sk_shutdown = SHUTDOWN_MASK
(!) flush accept_queue
    release
                                        virtio_transport_recv_pkt
                                          vsock_find_bound_socket
                                          lock
                                          if flag(SOCK_DONE) return
                                          virtio_transport_recv_listen
                                            child = vsock_create_connected
                                      (!)   vsock_enqueue_accept(child)
                                          release
close_work
  lock
  virtio_transport_do_close
    set_flag(SOCK_DONE)
    virtio_transport_remove_sock
      vsock_remove_sock
        vsock_remove_bound
  release

Introduce a sk_shutdown check to disallow vsock_enqueue_accept() during
socket destruction.

unreferenced object 0xffff888109e3f800 (size 2040):
  comm "kworker/5:2", pid 371, jiffies 4294940105
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    28 00 0b 40 00 00 00 00 00 00 00 00 00 00 00 00  (..@............
  backtrace (crc 9e5f4e84):
    [<ffffffff81418ff1>] kmem_cache_alloc_noprof+0x2c1/0x360
    [<ffffffff81d27aa0>] sk_prot_alloc+0x30/0x120
    [<ffffffff81d2b54c>] sk_alloc+0x2c/0x4b0
    [<ffffffff81fe049a>] __vsock_create.constprop.0+0x2a/0x310
    [<ffffffff81fe6d6c>] virtio_transport_recv_pkt+0x4dc/0x9a0
    [<ffffffff81fe745d>] vsock_loopback_work+0xfd/0x140
    [<ffffffff810fc6ac>] process_one_work+0x20c/0x570
    [<ffffffff810fce3f>] worker_thread+0x1bf/0x3a0
    [<ffffffff811070dd>] kthread+0xdd/0x110
    [<ffffffff81044fdd>] ret_from_fork+0x2d/0x50
    [<ffffffff8100785a>] ret_from_fork_asm+0x1a/0x30

Fixes: 3fe356d58efa ("vsock/virtio: discard packets only when socket is really closed")
Signed-off-by: Michal Luczaj <mhal@rbox.co>
---
 net/vmw_vsock/virtio_transport_common.c | 8 ++++++++
 1 file changed, 8 insertions(+)

Comments

Stefano Garzarella Nov. 7, 2024, 10:15 a.m. UTC | #1
On Wed, Nov 06, 2024 at 06:51:18PM +0100, Michal Luczaj wrote:
>As the final stages of socket destruction may be delayed, it is possible
>that virtio_transport_recv_listen() will be called after the accept_queue
>has been flushed, but before the SOCK_DONE flag has been set. As a result,
>sockets enqueued after the flush would remain unremoved, leading to a
>memory leak.
>
>vsock_release
>  __vsock_release
>    lock
>    virtio_transport_release
>      virtio_transport_close
>        schedule_delayed_work(close_work)
>    sk_shutdown = SHUTDOWN_MASK
>(!) flush accept_queue
>    release
>                                        virtio_transport_recv_pkt
>                                          vsock_find_bound_socket
>                                          lock
>                                          if flag(SOCK_DONE) return
>                                          virtio_transport_recv_listen
>                                            child = vsock_create_connected
>                                      (!)   vsock_enqueue_accept(child)
>                                          release
>close_work
>  lock
>  virtio_transport_do_close
>    set_flag(SOCK_DONE)
>    virtio_transport_remove_sock
>      vsock_remove_sock
>        vsock_remove_bound
>  release
>
>Introduce a sk_shutdown check to disallow vsock_enqueue_accept() during
>socket destruction.
>
>unreferenced object 0xffff888109e3f800 (size 2040):
>  comm "kworker/5:2", pid 371, jiffies 4294940105
>  hex dump (first 32 bytes):
>    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
>    28 00 0b 40 00 00 00 00 00 00 00 00 00 00 00 00  (..@............
>  backtrace (crc 9e5f4e84):
>    [<ffffffff81418ff1>] kmem_cache_alloc_noprof+0x2c1/0x360
>    [<ffffffff81d27aa0>] sk_prot_alloc+0x30/0x120
>    [<ffffffff81d2b54c>] sk_alloc+0x2c/0x4b0
>    [<ffffffff81fe049a>] __vsock_create.constprop.0+0x2a/0x310
>    [<ffffffff81fe6d6c>] virtio_transport_recv_pkt+0x4dc/0x9a0
>    [<ffffffff81fe745d>] vsock_loopback_work+0xfd/0x140
>    [<ffffffff810fc6ac>] process_one_work+0x20c/0x570
>    [<ffffffff810fce3f>] worker_thread+0x1bf/0x3a0
>    [<ffffffff811070dd>] kthread+0xdd/0x110
>    [<ffffffff81044fdd>] ret_from_fork+0x2d/0x50
>    [<ffffffff8100785a>] ret_from_fork_asm+0x1a/0x30
>
>Fixes: 3fe356d58efa ("vsock/virtio: discard packets only when socket is really closed")
>Signed-off-by: Michal Luczaj <mhal@rbox.co>
>---
> net/vmw_vsock/virtio_transport_common.c | 8 ++++++++
> 1 file changed, 8 insertions(+)

Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>

>
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index ccbd2bc0d2109aea4f19e79a0438f85893e1d89c..cd075f608d4f6f48f894543e5e9c966d3e5f22df 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -1512,6 +1512,14 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb,
> 		return -ENOMEM;
> 	}
>
>+	/* __vsock_release() might have already flushed accept_queue.
>+	 * Subsequent enqueues would lead to a memory leak.
>+	 */
>+	if (sk->sk_shutdown == SHUTDOWN_MASK) {
>+		virtio_transport_reset_no_sock(t, skb);
>+		return -ESHUTDOWN;
>+	}
>+
> 	child = vsock_create_connected(sk);
> 	if (!child) {
> 		virtio_transport_reset_no_sock(t, skb);
>
>-- 
>2.46.2
>
diff mbox series

Patch

diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index ccbd2bc0d2109aea4f19e79a0438f85893e1d89c..cd075f608d4f6f48f894543e5e9c966d3e5f22df 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1512,6 +1512,14 @@  virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb,
 		return -ENOMEM;
 	}
 
+	/* __vsock_release() might have already flushed accept_queue.
+	 * Subsequent enqueues would lead to a memory leak.
+	 */
+	if (sk->sk_shutdown == SHUTDOWN_MASK) {
+		virtio_transport_reset_no_sock(t, skb);
+		return -ESHUTDOWN;
+	}
+
 	child = vsock_create_connected(sk);
 	if (!child) {
 		virtio_transport_reset_no_sock(t, skb);