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