2023-11-03 17:57:45

by f.storniolo95

[permalink] [raw]
Subject: [PATCH net 0/4] vsock: fix server prevents clients from reconnecting

From: Filippo Storniolo <[email protected]>

This patch series introduce fix and tests for the following vsock bug:
If the same remote peer, using the same port, tries to connect
to a server on a listening port more than once, the server will
reject the connection, causing a "connection reset by peer"
error on the remote peer. This is due to the presence of a
dangling socket from a previous connection in both the connected
and bound socket lists.
The inconsistency of the above lists only occurs when the remote
peer disconnects and the server remains active.
This bug does not occur when the server socket is closed.

More details on the first patch changelog.
The remaining patches are refactoring and test.

Filippo Storniolo (4):
vsock/virtio: remove socket from connected/bound list on shutdown
test/vsock fix: add missing check on socket creation
test/vsock: refactor vsock_accept
test/vsock: add dobule bind connect test

net/vmw_vsock/virtio_transport_common.c | 16 +++--
tools/testing/vsock/util.c | 87 +++++++++++++++++++++----
tools/testing/vsock/util.h | 3 +
tools/testing/vsock/vsock_test.c | 50 ++++++++++++++
4 files changed, 139 insertions(+), 17 deletions(-)

--
2.41.0


2023-11-03 17:57:48

by f.storniolo95

[permalink] [raw]
Subject: [PATCH net 1/4] vsock/virtio: remove socket from connected/bound list on shutdown

From: Filippo Storniolo <[email protected]>

If the same remote peer, using the same port, tries to connect
to a server on a listening port more than once, the server will
reject the connection, causing a "connection reset by peer"
error on the remote peer. This is due to the presence of a
dangling socket from a previous connection in both the connected
and bound socket lists.
The inconsistency of the above lists only occurs when the remote
peer disconnects and the server remains active.

This bug does not occur when the server socket is closed:
virtio_transport_release() will eventually schedule a call to
virtio_transport_do_close() and the latter will remove the socket
from the bound and connected socket lists and clear the sk_buff.

However, virtio_transport_do_close() will only perform the above
actions if it has been scheduled, and this will not happen
if the server is processing the shutdown message from a remote peer.

To fix this, introduce a call to vsock_remove_sock()
when the server is handling a client disconnect.
This is to remove the socket from the bound and connected socket
lists without clearing the sk_buff.

Fixes: 06a8fc78367d ("VSOCK: Introduce virtio_vsock_common.ko")
Reported-by: Daan De Meyer <[email protected]>
Tested-by: Daan De Meyer <[email protected]>
Co-developed-by: Luigi Leonardi <[email protected]>
Signed-off-by: Luigi Leonardi <[email protected]>
Signed-off-by: Filippo Storniolo <[email protected]>
---
net/vmw_vsock/virtio_transport_common.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index e22c81435ef7..4c595dd1fd64 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1369,11 +1369,17 @@ virtio_transport_recv_connected(struct sock *sk,
vsk->peer_shutdown |= RCV_SHUTDOWN;
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
vsk->peer_shutdown |= SEND_SHUTDOWN;
- if (vsk->peer_shutdown == SHUTDOWN_MASK &&
- vsock_stream_has_data(vsk) <= 0 &&
- !sock_flag(sk, SOCK_DONE)) {
- (void)virtio_transport_reset(vsk, NULL);
- virtio_transport_do_close(vsk, true);
+ if (vsk->peer_shutdown == SHUTDOWN_MASK) {
+ if (vsock_stream_has_data(vsk) <= 0 && !sock_flag(sk, SOCK_DONE)) {
+ (void)virtio_transport_reset(vsk, NULL);
+ virtio_transport_do_close(vsk, true);
+ }
+ /* Remove this socket anyway because the remote peer sent
+ * the shutdown. This way a new connection will succeed
+ * if the remote peer uses the same source port,
+ * even if the old socket is still unreleased, but now disconnected.
+ */
+ vsock_remove_sock(vsk);
}
if (le32_to_cpu(virtio_vsock_hdr(skb)->flags))
sk->sk_state_change(sk);
--
2.41.0

2023-11-03 17:58:04

by f.storniolo95

[permalink] [raw]
Subject: [PATCH net 2/4] test/vsock fix: add missing check on socket creation

From: Filippo Storniolo <[email protected]>

Add check on socket() return value in vsock_listen()
and vsock_connect()

Co-developed-by: Luigi Leonardi <[email protected]>
Signed-off-by: Luigi Leonardi <[email protected]>
Signed-off-by: Filippo Storniolo <[email protected]>
---
tools/testing/vsock/util.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
index 92336721321a..698b0b44a2ee 100644
--- a/tools/testing/vsock/util.c
+++ b/tools/testing/vsock/util.c
@@ -104,6 +104,10 @@ static int vsock_connect(unsigned int cid, unsigned int port, int type)
control_expectln("LISTENING");

fd = socket(AF_VSOCK, type, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }

timeout_begin(TIMEOUT);
do {
@@ -158,6 +162,10 @@ static int vsock_accept(unsigned int cid, unsigned int port,
int old_errno;

fd = socket(AF_VSOCK, type, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }

if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
perror("bind");
--
2.41.0

2023-11-03 17:58:59

by f.storniolo95

[permalink] [raw]
Subject: [PATCH net 3/4] test/vsock: refactor vsock_accept

From: Filippo Storniolo <[email protected]>

This is a preliminary patch to introduce SOCK_STREAM bind connect test.
vsock_accept() is split into vsock_listen() and vsock_accept().

Co-developed-by: Luigi Leonardi <[email protected]>
Signed-off-by: Luigi Leonardi <[email protected]>
Signed-off-by: Filippo Storniolo <[email protected]>
---
tools/testing/vsock/util.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
index 698b0b44a2ee..2fc96f29bdf2 100644
--- a/tools/testing/vsock/util.c
+++ b/tools/testing/vsock/util.c
@@ -136,11 +136,8 @@ int vsock_seqpacket_connect(unsigned int cid, unsigned int port)
return vsock_connect(cid, port, SOCK_SEQPACKET);
}

-/* Listen on <cid, port> and return the first incoming connection. The remote
- * address is stored to clientaddrp. clientaddrp may be NULL.
- */
-static int vsock_accept(unsigned int cid, unsigned int port,
- struct sockaddr_vm *clientaddrp, int type)
+/* Listen on <cid, port> and return the file descriptor. */
+static int vsock_listen(unsigned int cid, unsigned int port, int type)
{
union {
struct sockaddr sa;
@@ -152,14 +149,7 @@ static int vsock_accept(unsigned int cid, unsigned int port,
.svm_cid = cid,
},
};
- union {
- struct sockaddr sa;
- struct sockaddr_vm svm;
- } clientaddr;
- socklen_t clientaddr_len = sizeof(clientaddr.svm);
int fd;
- int client_fd;
- int old_errno;

fd = socket(AF_VSOCK, type, 0);
if (fd < 0) {
@@ -177,6 +167,24 @@ static int vsock_accept(unsigned int cid, unsigned int port,
exit(EXIT_FAILURE);
}

+ return fd;
+}
+
+/* Listen on <cid, port> and return the first incoming connection. The remote
+ * address is stored to clientaddrp. clientaddrp may be NULL.
+ */
+static int vsock_accept(unsigned int cid, unsigned int port,
+ struct sockaddr_vm *clientaddrp, int type)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_vm svm;
+ } clientaddr;
+ socklen_t clientaddr_len = sizeof(clientaddr.svm);
+ int fd, client_fd, old_errno;
+
+ fd = vsock_listen(cid, port, type);
+
control_writeln("LISTENING");

timeout_begin(TIMEOUT);
--
2.41.0

2023-11-03 17:58:59

by f.storniolo95

[permalink] [raw]
Subject: [PATCH net 4/4] test/vsock: add dobule bind connect test

From: Filippo Storniolo <[email protected]>

This add bind connect test which creates a listening server socket
and tries to connect a client with a bound local port to it twice.

Co-developed-by: Luigi Leonardi <[email protected]>
Signed-off-by: Luigi Leonardi <[email protected]>
Signed-off-by: Filippo Storniolo <[email protected]>
---
tools/testing/vsock/util.c | 47 ++++++++++++++++++++++++++++++
tools/testing/vsock/util.h | 3 ++
tools/testing/vsock/vsock_test.c | 50 ++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+)

diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
index 2fc96f29bdf2..ae2b33c21c45 100644
--- a/tools/testing/vsock/util.c
+++ b/tools/testing/vsock/util.c
@@ -85,6 +85,48 @@ void vsock_wait_remote_close(int fd)
close(epollfd);
}

+/* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */
+int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type)
+{
+ struct sockaddr_vm sa_client = {
+ .svm_family = AF_VSOCK,
+ .svm_cid = VMADDR_CID_ANY,
+ .svm_port = bind_port,
+ };
+ struct sockaddr_vm sa_server = {
+ .svm_family = AF_VSOCK,
+ .svm_cid = cid,
+ .svm_port = port,
+ };
+
+ int client_fd, ret;
+
+ client_fd = socket(AF_VSOCK, type, 0);
+ if (client_fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (bind(client_fd, (struct sockaddr *)&sa_client, sizeof(sa_client))) {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+
+ timeout_begin(TIMEOUT);
+ do {
+ ret = connect(client_fd, (struct sockaddr *)&sa_server, sizeof(sa_server));
+ timeout_check("connect");
+ } while (ret < 0 && errno == EINTR);
+ timeout_end();
+
+ if (ret < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ return client_fd;
+}
+
/* Connect to <cid, port> and return the file descriptor. */
static int vsock_connect(unsigned int cid, unsigned int port, int type)
{
@@ -223,6 +265,11 @@ int vsock_stream_accept(unsigned int cid, unsigned int port,
return vsock_accept(cid, port, clientaddrp, SOCK_STREAM);
}

+int vsock_stream_listen(unsigned int cid, unsigned int port)
+{
+ return vsock_listen(cid, port, SOCK_STREAM);
+}
+
int vsock_seqpacket_accept(unsigned int cid, unsigned int port,
struct sockaddr_vm *clientaddrp)
{
diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h
index a77175d25864..03c88d0cb861 100644
--- a/tools/testing/vsock/util.h
+++ b/tools/testing/vsock/util.h
@@ -36,9 +36,12 @@ struct test_case {
void init_signals(void);
unsigned int parse_cid(const char *str);
int vsock_stream_connect(unsigned int cid, unsigned int port);
+int vsock_bind_connect(unsigned int cid, unsigned int port,
+ unsigned int bind_port, int type);
int vsock_seqpacket_connect(unsigned int cid, unsigned int port);
int vsock_stream_accept(unsigned int cid, unsigned int port,
struct sockaddr_vm *clientaddrp);
+int vsock_stream_listen(unsigned int cid, unsigned int port);
int vsock_seqpacket_accept(unsigned int cid, unsigned int port,
struct sockaddr_vm *clientaddrp);
void vsock_wait_remote_close(int fd);
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index c1f7bc9abd22..5b0e93f9996c 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -1180,6 +1180,51 @@ static void test_stream_shutrd_server(const struct test_opts *opts)
close(fd);
}

+static void test_double_bind_connect_server(const struct test_opts *opts)
+{
+ int listen_fd, client_fd, i;
+ struct sockaddr_vm sa_client;
+ socklen_t socklen_client = sizeof(sa_client);
+
+ listen_fd = vsock_stream_listen(VMADDR_CID_ANY, 1234);
+
+ for (i = 0; i < 2; i++) {
+ control_writeln("LISTENING");
+
+ timeout_begin(TIMEOUT);
+ do {
+ client_fd = accept(listen_fd, (struct sockaddr *)&sa_client,
+ &socklen_client);
+ timeout_check("accept");
+ } while (client_fd < 0 && errno == EINTR);
+ timeout_end();
+
+ if (client_fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Waiting for remote peer to close connection */
+ vsock_wait_remote_close(client_fd);
+ }
+
+ close(listen_fd);
+}
+
+static void test_double_bind_connect_client(const struct test_opts *opts)
+{
+ int i, client_fd;
+
+ for (i = 0; i < 2; i++) {
+ /* Wait until server is ready to accept a new connection */
+ control_expectln("LISTENING");
+
+ client_fd = vsock_bind_connect(opts->peer_cid, 1234, 4321, SOCK_STREAM);
+
+ close(client_fd);
+ }
+}
+
static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
@@ -1285,6 +1330,11 @@ static struct test_case test_cases[] = {
.run_client = test_stream_msgzcopy_empty_errq_client,
.run_server = test_stream_msgzcopy_empty_errq_server,
},
+ {
+ .name = "SOCK_STREAM double bind connect",
+ .run_client = test_double_bind_connect_client,
+ .run_server = test_double_bind_connect_server,
+ },
{},
};

--
2.41.0

2023-11-06 10:44:54

by Stefano Garzarella

[permalink] [raw]
Subject: Re: [PATCH net 1/4] vsock/virtio: remove socket from connected/bound list on shutdown

On Fri, Nov 03, 2023 at 06:55:48PM +0100, [email protected] wrote:
>From: Filippo Storniolo <[email protected]>
>
>If the same remote peer, using the same port, tries to connect
>to a server on a listening port more than once, the server will
>reject the connection, causing a "connection reset by peer"
>error on the remote peer. This is due to the presence of a
>dangling socket from a previous connection in both the connected
>and bound socket lists.
>The inconsistency of the above lists only occurs when the remote
>peer disconnects and the server remains active.
>
>This bug does not occur when the server socket is closed:
>virtio_transport_release() will eventually schedule a call to
>virtio_transport_do_close() and the latter will remove the socket
>from the bound and connected socket lists and clear the sk_buff.
>
>However, virtio_transport_do_close() will only perform the above
>actions if it has been scheduled, and this will not happen
>if the server is processing the shutdown message from a remote peer.
>
>To fix this, introduce a call to vsock_remove_sock()
>when the server is handling a client disconnect.
>This is to remove the socket from the bound and connected socket
>lists without clearing the sk_buff.
>
>Fixes: 06a8fc78367d ("VSOCK: Introduce virtio_vsock_common.ko")
>Reported-by: Daan De Meyer <[email protected]>
>Tested-by: Daan De Meyer <[email protected]>
>Co-developed-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Filippo Storniolo <[email protected]>
>---
> net/vmw_vsock/virtio_transport_common.c | 16 +++++++++++-----
> 1 file changed, 11 insertions(+), 5 deletions(-)
>
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index e22c81435ef7..4c595dd1fd64 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -1369,11 +1369,17 @@ virtio_transport_recv_connected(struct sock *sk,
> vsk->peer_shutdown |= RCV_SHUTDOWN;
> if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
> vsk->peer_shutdown |= SEND_SHUTDOWN;
>- if (vsk->peer_shutdown == SHUTDOWN_MASK &&
>- vsock_stream_has_data(vsk) <= 0 &&
>- !sock_flag(sk, SOCK_DONE)) {
>- (void)virtio_transport_reset(vsk, NULL);
>- virtio_transport_do_close(vsk, true);
>+ if (vsk->peer_shutdown == SHUTDOWN_MASK) {
>+ if (vsock_stream_has_data(vsk) <= 0 && !sock_flag(sk, SOCK_DONE)) {
>+ (void)virtio_transport_reset(vsk, NULL);
>+ virtio_transport_do_close(vsk, true);
>+ }
>+ /* Remove this socket anyway because the remote peer sent
>+ * the shutdown. This way a new connection will succeed
>+ * if the remote peer uses the same source port,
>+ * even if the old socket is still unreleased, but now disconnected.
>+ */
>+ vsock_remove_sock(vsk);
> }
> if (le32_to_cpu(virtio_vsock_hdr(skb)->flags))
> sk->sk_state_change(sk);
>--
>2.41.0
>

Thanks for fixing this issue! LGTM.

Just to inform other maintainers as well. Daan reported this issue to me
at DevConf.cz, I shared it with Filippo and Luigi who analyzed and
solved it.

Reviewed-by: Stefano Garzarella <[email protected]>


2023-11-06 10:47:37

by Stefano Garzarella

[permalink] [raw]
Subject: Re: [PATCH net 2/4] test/vsock fix: add missing check on socket creation

On Fri, Nov 03, 2023 at 06:55:49PM +0100, [email protected] wrote:
>From: Filippo Storniolo <[email protected]>
>
>Add check on socket() return value in vsock_listen()
>and vsock_connect()
>
>Co-developed-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Filippo Storniolo <[email protected]>
>---
> tools/testing/vsock/util.c | 8 ++++++++
> 1 file changed, 8 insertions(+)

If you need to resend the entire series, maybe you can remove "fix"
from the commit title.

But it's a minor thing, so I would only change it if there's something
else that justifies sending a v2:

Reviewed-by: Stefano Garzarella <[email protected]>

>
>diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
>index 92336721321a..698b0b44a2ee 100644
>--- a/tools/testing/vsock/util.c
>+++ b/tools/testing/vsock/util.c
>@@ -104,6 +104,10 @@ static int vsock_connect(unsigned int cid, unsigned int port, int type)
> control_expectln("LISTENING");
>
> fd = socket(AF_VSOCK, type, 0);
>+ if (fd < 0) {
>+ perror("socket");
>+ exit(EXIT_FAILURE);
>+ }
>
> timeout_begin(TIMEOUT);
> do {
>@@ -158,6 +162,10 @@ static int vsock_accept(unsigned int cid, unsigned int port,
> int old_errno;
>
> fd = socket(AF_VSOCK, type, 0);
>+ if (fd < 0) {
>+ perror("socket");
>+ exit(EXIT_FAILURE);
>+ }
>
> if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
> perror("bind");
>--
>2.41.0
>

2023-11-06 10:48:53

by Stefano Garzarella

[permalink] [raw]
Subject: Re: [PATCH net 3/4] test/vsock: refactor vsock_accept

On Fri, Nov 03, 2023 at 06:55:50PM +0100, [email protected] wrote:
>From: Filippo Storniolo <[email protected]>
>
>This is a preliminary patch to introduce SOCK_STREAM bind connect test.
>vsock_accept() is split into vsock_listen() and vsock_accept().
>
>Co-developed-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Filippo Storniolo <[email protected]>
>---
> tools/testing/vsock/util.c | 32 ++++++++++++++++++++------------
> 1 file changed, 20 insertions(+), 12 deletions(-)

LGTM!

Reviewed-by: Stefano Garzarella <[email protected]>

>
>diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
>index 698b0b44a2ee..2fc96f29bdf2 100644
>--- a/tools/testing/vsock/util.c
>+++ b/tools/testing/vsock/util.c
>@@ -136,11 +136,8 @@ int vsock_seqpacket_connect(unsigned int cid, unsigned int port)
> return vsock_connect(cid, port, SOCK_SEQPACKET);
> }
>
>-/* Listen on <cid, port> and return the first incoming connection. The remote
>- * address is stored to clientaddrp. clientaddrp may be NULL.
>- */
>-static int vsock_accept(unsigned int cid, unsigned int port,
>- struct sockaddr_vm *clientaddrp, int type)
>+/* Listen on <cid, port> and return the file descriptor. */
>+static int vsock_listen(unsigned int cid, unsigned int port, int type)
> {
> union {
> struct sockaddr sa;
>@@ -152,14 +149,7 @@ static int vsock_accept(unsigned int cid, unsigned int port,
> .svm_cid = cid,
> },
> };
>- union {
>- struct sockaddr sa;
>- struct sockaddr_vm svm;
>- } clientaddr;
>- socklen_t clientaddr_len = sizeof(clientaddr.svm);
> int fd;
>- int client_fd;
>- int old_errno;
>
> fd = socket(AF_VSOCK, type, 0);
> if (fd < 0) {
>@@ -177,6 +167,24 @@ static int vsock_accept(unsigned int cid, unsigned int port,
> exit(EXIT_FAILURE);
> }
>
>+ return fd;
>+}
>+
>+/* Listen on <cid, port> and return the first incoming connection. The remote
>+ * address is stored to clientaddrp. clientaddrp may be NULL.
>+ */
>+static int vsock_accept(unsigned int cid, unsigned int port,
>+ struct sockaddr_vm *clientaddrp, int type)
>+{
>+ union {
>+ struct sockaddr sa;
>+ struct sockaddr_vm svm;
>+ } clientaddr;
>+ socklen_t clientaddr_len = sizeof(clientaddr.svm);
>+ int fd, client_fd, old_errno;
>+
>+ fd = vsock_listen(cid, port, type);
>+
> control_writeln("LISTENING");
>
> timeout_begin(TIMEOUT);
>--
>2.41.0
>

2023-11-06 10:49:33

by Stefano Garzarella

[permalink] [raw]
Subject: Re: [PATCH net 4/4] test/vsock: add dobule bind connect test

On Fri, Nov 03, 2023 at 06:55:51PM +0100, [email protected] wrote:
>From: Filippo Storniolo <[email protected]>
>
>This add bind connect test which creates a listening server socket
>and tries to connect a client with a bound local port to it twice.
>
>Co-developed-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Luigi Leonardi <[email protected]>
>Signed-off-by: Filippo Storniolo <[email protected]>
>---
> tools/testing/vsock/util.c | 47 ++++++++++++++++++++++++++++++
> tools/testing/vsock/util.h | 3 ++
> tools/testing/vsock/vsock_test.c | 50 ++++++++++++++++++++++++++++++++
> 3 files changed, 100 insertions(+)

LGTM!

Reviewed-by: Stefano Garzarella <[email protected]>

>
>diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
>index 2fc96f29bdf2..ae2b33c21c45 100644
>--- a/tools/testing/vsock/util.c
>+++ b/tools/testing/vsock/util.c
>@@ -85,6 +85,48 @@ void vsock_wait_remote_close(int fd)
> close(epollfd);
> }
>
>+/* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */
>+int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type)
>+{
>+ struct sockaddr_vm sa_client = {
>+ .svm_family = AF_VSOCK,
>+ .svm_cid = VMADDR_CID_ANY,
>+ .svm_port = bind_port,
>+ };
>+ struct sockaddr_vm sa_server = {
>+ .svm_family = AF_VSOCK,
>+ .svm_cid = cid,
>+ .svm_port = port,
>+ };
>+
>+ int client_fd, ret;
>+
>+ client_fd = socket(AF_VSOCK, type, 0);
>+ if (client_fd < 0) {
>+ perror("socket");
>+ exit(EXIT_FAILURE);
>+ }
>+
>+ if (bind(client_fd, (struct sockaddr *)&sa_client, sizeof(sa_client))) {
>+ perror("bind");
>+ exit(EXIT_FAILURE);
>+ }
>+
>+ timeout_begin(TIMEOUT);
>+ do {
>+ ret = connect(client_fd, (struct sockaddr *)&sa_server, sizeof(sa_server));
>+ timeout_check("connect");
>+ } while (ret < 0 && errno == EINTR);
>+ timeout_end();
>+
>+ if (ret < 0) {
>+ perror("connect");
>+ exit(EXIT_FAILURE);
>+ }
>+
>+ return client_fd;
>+}
>+
> /* Connect to <cid, port> and return the file descriptor. */
> static int vsock_connect(unsigned int cid, unsigned int port, int type)
> {
>@@ -223,6 +265,11 @@ int vsock_stream_accept(unsigned int cid, unsigned int port,
> return vsock_accept(cid, port, clientaddrp, SOCK_STREAM);
> }
>
>+int vsock_stream_listen(unsigned int cid, unsigned int port)
>+{
>+ return vsock_listen(cid, port, SOCK_STREAM);
>+}
>+
> int vsock_seqpacket_accept(unsigned int cid, unsigned int port,
> struct sockaddr_vm *clientaddrp)
> {
>diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h
>index a77175d25864..03c88d0cb861 100644
>--- a/tools/testing/vsock/util.h
>+++ b/tools/testing/vsock/util.h
>@@ -36,9 +36,12 @@ struct test_case {
> void init_signals(void);
> unsigned int parse_cid(const char *str);
> int vsock_stream_connect(unsigned int cid, unsigned int port);
>+int vsock_bind_connect(unsigned int cid, unsigned int port,
>+ unsigned int bind_port, int type);
> int vsock_seqpacket_connect(unsigned int cid, unsigned int port);
> int vsock_stream_accept(unsigned int cid, unsigned int port,
> struct sockaddr_vm *clientaddrp);
>+int vsock_stream_listen(unsigned int cid, unsigned int port);
> int vsock_seqpacket_accept(unsigned int cid, unsigned int port,
> struct sockaddr_vm *clientaddrp);
> void vsock_wait_remote_close(int fd);
>diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
>index c1f7bc9abd22..5b0e93f9996c 100644
>--- a/tools/testing/vsock/vsock_test.c
>+++ b/tools/testing/vsock/vsock_test.c
>@@ -1180,6 +1180,51 @@ static void test_stream_shutrd_server(const struct test_opts *opts)
> close(fd);
> }
>
>+static void test_double_bind_connect_server(const struct test_opts *opts)
>+{
>+ int listen_fd, client_fd, i;
>+ struct sockaddr_vm sa_client;
>+ socklen_t socklen_client = sizeof(sa_client);
>+
>+ listen_fd = vsock_stream_listen(VMADDR_CID_ANY, 1234);
>+
>+ for (i = 0; i < 2; i++) {
>+ control_writeln("LISTENING");
>+
>+ timeout_begin(TIMEOUT);
>+ do {
>+ client_fd = accept(listen_fd, (struct sockaddr *)&sa_client,
>+ &socklen_client);
>+ timeout_check("accept");
>+ } while (client_fd < 0 && errno == EINTR);
>+ timeout_end();
>+
>+ if (client_fd < 0) {
>+ perror("accept");
>+ exit(EXIT_FAILURE);
>+ }
>+
>+ /* Waiting for remote peer to close connection */
>+ vsock_wait_remote_close(client_fd);
>+ }
>+
>+ close(listen_fd);
>+}
>+
>+static void test_double_bind_connect_client(const struct test_opts *opts)
>+{
>+ int i, client_fd;
>+
>+ for (i = 0; i < 2; i++) {
>+ /* Wait until server is ready to accept a new connection */
>+ control_expectln("LISTENING");
>+
>+ client_fd = vsock_bind_connect(opts->peer_cid, 1234, 4321, SOCK_STREAM);
>+
>+ close(client_fd);
>+ }
>+}
>+
> static struct test_case test_cases[] = {
> {
> .name = "SOCK_STREAM connection reset",
>@@ -1285,6 +1330,11 @@ static struct test_case test_cases[] = {
> .run_client = test_stream_msgzcopy_empty_errq_client,
> .run_server = test_stream_msgzcopy_empty_errq_server,
> },
>+ {
>+ .name = "SOCK_STREAM double bind connect",
>+ .run_client = test_double_bind_connect_client,
>+ .run_server = test_double_bind_connect_server,
>+ },
> {},
> };
>
>--
>2.41.0
>

2023-11-06 10:52:12

by Stefano Garzarella

[permalink] [raw]
Subject: Re: [PATCH net 0/4] vsock: fix server prevents clients from reconnecting

On Fri, Nov 03, 2023 at 06:55:47PM +0100, [email protected] wrote:
>From: Filippo Storniolo <[email protected]>
>
>This patch series introduce fix and tests for the following vsock bug:
>If the same remote peer, using the same port, tries to connect
>to a server on a listening port more than once, the server will
>reject the connection, causing a "connection reset by peer"
>error on the remote peer. This is due to the presence of a
>dangling socket from a previous connection in both the connected
>and bound socket lists.
>The inconsistency of the above lists only occurs when the remote
>peer disconnects and the server remains active.
>This bug does not occur when the server socket is closed.
>
>More details on the first patch changelog.
>The remaining patches are refactoring and test.

Thanks for the fix and the test!

I only left a small comment in patch 2 which I don't think justifies a
v2 by itself though. If for some other reason you have to send a v2,
then maybe I would fix it.

I reviewed the series and ran the tests. Everything seems to be fine.

Thanks,
Stefano