hvs_open_connection() only checks sk_state == TCP_LISTEN and sk_acceptq_is_full() before creating a child and calling vsock_enqueue_accept(). __vsock_release() sets sk->sk_shutdown = SHUTDOWN_MASK and flushes the accept queue while leaving sk_state == TCP_LISTEN, so a host channel offer that races close() of the listener passes both checks and enqueues a child onto the already-flushed queue. That child socket (and the VMBUS channel opened for it) is never accepted or cleaned up and leaks.
The virtio transport guards exactly this case in virtio_transport_recv_listen(); hv_sock lacks the equivalent guard. hv_sock holds lock_sock(sk) across hvs_open_connection(), so the check is race-free. Signed-off-by: Bartłomiej Dmitruk <[email protected]> --- diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -324,6 +324,14 @@ if (conn_from_host) { if (sk_acceptq_is_full(sk)) + goto out; + + /* __vsock_release() may have already flushed the accept queue + * and set sk_shutdown = SHUTDOWN_MASK while leaving sk_state == + * TCP_LISTEN. Enqueuing a child now would leak the child socket + * and its VMBUS channel. Mirror virtio_transport_recv_listen(). + */ + if (sk->sk_shutdown == SHUTDOWN_MASK) goto out; new = vsock_create_connected(sk);
