Transport events are serviced by resetting each connected socket. The reset
is done under vsock_table_lock but without taking lock_sock(), so from the
point of view of vsock_connect() - locklessly. The same pattern is present
in virtio_vsock_reset_sock(), vmci_transport_handle_detach() and
vhost_vsock_reset_orphans().

While connect() waits for TCP_SYN_SENT -> TCP_ESTABLISHED, other
transitions can also occur:

  TCP_SYN_SENT -> TCP_CLOSE on connection failure, timeout or signal
  TCP_SYN_SENT -> TCP_ESTABLISHED -> TCP_CLOSING on VIRTIO_VSOCK_OP_RST
  TCP_SYN_SENT -> TCP_ESTABLISHED -> [TCP_CLOSING ->] TCP_CLOSE on event

But transport events leave SS_CONNECTED state unchanged. So take a note of
that, rather than a) making every event handler drop the socket from
connected_table, or b) adapting connect() to handle more transitions (while
missing proper locking).

Fixes: d021c344051a ("VSOCK: Introduce VM Sockets")
Signed-off-by: Michal Luczaj <[email protected]>
---
 net/vmw_vsock/af_vsock.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index ff4140aaf1f3..5a2d7e10ecb8 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1663,6 +1663,19 @@ static void vsock_unconnected_reset(struct sock *sk)
 
        sock_owned_by_me(sk);
 
+       /*
+        * Transport events are serviced by resetting each connected socket to
+        * TCP_CLOSE. The reset is done under vsock_table_lock but without
+        * taking lock_sock(), so it is effectively lockless from the
+        * perspective of connect().
+        *
+        * Preserve socket's bricked status. Also check SS_DISCONNECTING, since
+        * shutdown() may race us.
+        */
+       if (sk->sk_socket->state == SS_CONNECTED ||
+           sk->sk_socket->state == SS_DISCONNECTING)
+               return;
+
        /*
         * Only connected socks may have peer_shutdown or SOCK_DONE set.
         *

-- 
2.55.0


Reply via email to