Since commit c0cfa2d8a788 ("vsock: add multi-transports support"),
bind() accepts any local CID, but connect() picks the transport from the
remote CID only.  The virtio transports send from the transport's CID,
so a socket bound to another local CID sends as if it were bound to
the same port on the transport's CID, and its connect() never
completes.

Over vsock_loopback, one process can use this against another that is
bound to (VMADDR_CID_LOCAL, P):
 1. it binds to (guest CID, P) and connects to a local listener; the
    request arrives from (VMADDR_CID_LOCAL, P) and leaves behind a
    child socket for that address, which stays until the listener
    accepts and closes it;
 2. the other process's connect() to the same listener matches that
    child and fails with ECONNRESET.

Refuse such a connect() with EADDRNOTAVAIL, as bind() does for a CID
that is not local.  And whenever the virtio connect() fails, set the
socket to TCP_CLOSE, as vmci_transport_connect() does, instead of
leaving it in TCP_SYN_SENT.

Found by an LLM-driven code audit.  I reproduced it on unmodified
kernels with the two processes running as different users, without
error injection, and tested the fix with the reproducer and vsock_test
in guests and on a host.  Drafted with Claude.

Fixes: c0cfa2d8a788 ("vsock: add multi-transports support")
Signed-off-by: Yiran Qiu <[email protected]>
---
 net/vmw_vsock/virtio_transport_common.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/net/vmw_vsock/virtio_transport_common.c 
b/net/vmw_vsock/virtio_transport_common.c
index 1e762a480df4..8f524d073ae1 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1219,8 +1219,20 @@ int virtio_transport_connect(struct vsock_sock *vsk)
                .vsk = vsk,
                .net = sock_net(sk_vsock(vsk)),
        };
+       u32 local_cid;
+       int ret;
 
-       return virtio_transport_send_pkt_info(vsk, &info);
+       local_cid = vsk->transport->get_local_cid();
+       if (local_cid != VMADDR_CID_ANY &&
+           vsk->local_addr.svm_cid != VMADDR_CID_ANY &&
+           vsk->local_addr.svm_cid != local_cid)
+               ret = -EADDRNOTAVAIL;
+       else
+               ret = virtio_transport_send_pkt_info(vsk, &info);
+
+       if (ret < 0)
+               sk_vsock(vsk)->sk_state = TCP_CLOSE;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(virtio_transport_connect);
 
-- 
2.56.0


Reply via email to