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