summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-08-18 14:58:55 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-08-18 14:58:55 +0200
commita3dee9bb902ee4357fa02e49b415d7724ee0140a (patch)
treed0b5f65a3f36aa9665c563c6c0058a3c8cd08f96
parente36ce6e78fe3fc3c071a26750783b7ba081ce10d (diff)
parent96cbf89993091a163bfedec52a3bd683dc94b3b4 (diff)
Merge branch 'vsock-fix-stale-sk_err-handling-after-a-failed-connect'
Nguyen Dinh Phi says: ==================== vsock: fix stale sk_err handling after a failed connect A socket whose connect() failed keeps sk_err set. If that socket is later reused as a listener, vsock_accept() rejects an unrelated incoming connection, and on virtio/hyperv the resulting child socket is leaked. Patch 1 removes the listener's sk_err check from vsock_accept(), since no vsock transport ever sets sk_err on a TCP_LISTEN socket. This will fix what the syzbot reported. Patch 2 removes vsock_sock.rejected, now unreachable after patch 1. Patch 3 is a related but separate fix: vsock_connect() now consumes sk_err via sock_error() once it has been returned to userspace, so a failed blocking connect() doesn't keep reporting the same error a second time. ==================== Link: https://patch.msgid.link/20260813173024.2362935-1-phind.uet@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--include/net/af_vsock.h5
-rw-r--r--net/vmw_vsock/af_vsock.c62
2 files changed, 21 insertions, 46 deletions
diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h
index 30046a3c20f7..3357ee62d10b 100644
--- a/include/net/af_vsock.h
+++ b/include/net/af_vsock.h
@@ -52,13 +52,10 @@ struct vsock_sock {
* The listening socket is the head for both lists. Sockets created
* for connection requests are placed in the pending list until they
* are connected, at which point they are put in the accept queue list
- * so they can be accepted in accept(). If accept() cannot accept the
- * connection, it is marked as rejected so the cleanup function knows
- * to clean up the socket.
+ * so they can be accepted in accept().
*/
struct list_head pending_links;
struct list_head accept_queue;
- bool rejected;
struct delayed_work connect_work;
struct delayed_work pending_work;
struct delayed_work close_work;
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 622dbd046799..e89cb84b8d73 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -38,10 +38,9 @@
* pending socket. When that socket reaches the connected state, it is removed
* from the listener socket's pending list and enqueued in the listener
* socket's accept queue. Callers of accept(2) will accept connected sockets
- * from the listener socket's accept queue. If the socket cannot be accepted
- * for some reason then it is marked rejected. Once the connection is
- * accepted, it is owned by the user process and the responsibility for cleanup
- * falls with that user process.
+ * from the listener socket's accept queue. Once the connection is accepted,
+ * it is owned by the user process and the responsibility for cleanup falls
+ * with that user process.
*
* - It is possible that these pending sockets will never reach the connected
* state; in fact, we may never receive another packet after the connection
@@ -49,9 +48,7 @@
* future, after some amount of time passes where a connection should have been
* established. This function ensures that the socket is off all lists so it
* cannot be retrieved, then drops all references to the socket so it is cleaned
- * up (sock_put() -> sk_free() -> our sk_destruct implementation). Note this
- * function will also cleanup rejected sockets, those that reach the connected
- * state but leave it before they have been accepted.
+ * up (sock_put() -> sk_free() -> our sk_destruct implementation).
*
* - Lock ordering for pending or accept queue sockets is:
*
@@ -774,11 +771,10 @@ static void vsock_pending_work(struct work_struct *work)
if (vsock_is_pending(sk)) {
vsock_remove_pending(listener, sk);
- } else if (!vsk->rejected) {
- /* We are not on the pending list and accept() did not reject
- * us, so we must have been accepted by our user process. We
- * just need to drop our references to the sockets and be on
- * our way.
+ } else {
+ /* We are not on the pending list so we must have been accepted
+ * by our user process. We just need to drop our references to
+ * the sockets and be on our way.
*/
cleanup = false;
goto out;
@@ -942,7 +938,6 @@ static struct sock *__vsock_create(struct net *net,
vsk->listener = NULL;
INIT_LIST_HEAD(&vsk->pending_links);
INIT_LIST_HEAD(&vsk->accept_queue);
- vsk->rejected = false;
vsk->sent_request = false;
vsk->ignore_connecting_rst = false;
WRITE_ONCE(vsk->peer_shutdown, 0);
@@ -1847,12 +1842,10 @@ static int vsock_connect(struct socket *sock, struct sockaddr_unsized *addr,
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
}
- if (sk->sk_err) {
- err = -sk->sk_err;
+ err = sock_error(sk);
+ if (err) {
sk->sk_state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
- } else {
- err = 0;
}
out_wait:
@@ -1893,7 +1886,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
timeout = sock_rcvtimeo(listener, arg->flags & O_NONBLOCK);
while ((connected = vsock_dequeue_accept(listener)) == NULL &&
- listener->sk_err == 0 && timeout != 0) {
+ timeout != 0) {
prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
release_sock(listener);
timeout = schedule_timeout(timeout);
@@ -1906,38 +1899,23 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
}
}
- if (listener->sk_err) {
- err = -listener->sk_err;
- } else if (!connected) {
+ if (!connected) {
err = -EAGAIN;
- }
-
- if (connected) {
+ } else {
sk_acceptq_removed(listener);
lock_sock_nested(connected, SINGLE_DEPTH_NESTING);
vconnected = vsock_sk(connected);
- /* If the listener socket has received an error, then we should
- * reject this socket and return. Note that we simply mark the
- * socket rejected, drop our reference, and let the cleanup
- * function handle the cleanup; the fact that we found it in
- * the listener's accept queue guarantees that the cleanup
- * function hasn't run yet.
- */
- if (err) {
- vconnected->rejected = true;
- } else {
- newsock->state = SS_CONNECTED;
- sock_graft(connected, newsock);
+ newsock->state = SS_CONNECTED;
+ sock_graft(connected, newsock);
- set_bit(SOCK_CUSTOM_SOCKOPT,
- &connected->sk_socket->flags);
+ set_bit(SOCK_CUSTOM_SOCKOPT,
+ &connected->sk_socket->flags);
- if (vsock_msgzerocopy_allow(vconnected->transport))
- set_bit(SOCK_SUPPORT_ZC,
- &connected->sk_socket->flags);
- }
+ if (vsock_msgzerocopy_allow(vconnected->transport))
+ set_bit(SOCK_SUPPORT_ZC,
+ &connected->sk_socket->flags);
release_sock(connected);
sock_put(connected);