* [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released
@ 2026-09-17 22:01 Bartłomiej Dmitruk
2026-09-18 13:17 ` Stefano Garzarella
2026-09-21 23:17 ` netdev-bot+sashiko
0 siblings, 2 replies; 3+ messages in thread
From: Bartłomiej Dmitruk @ 2026-09-17 22:01 UTC (permalink / raw)
To: Dexuan Cui, Wei Liu, Haiyang Zhang, K . Y . Srinivasan
Cc: linux-hyperv, netdev, Stefano Garzarella, Michael S . Tsirkin
hvs_open_connection() only checks sk_state == TCP_LISTEN and
sk_acceptq_is_full() before creating a child and calling
vsock_enqueue_accept(). __vsock_release() sets sk->sk_shutdown =
SHUTDOWN_MASK and flushes the accept queue while leaving sk_state ==
TCP_LISTEN, so a host channel offer that races close() of the listener
passes both checks and enqueues a child onto the already-flushed queue.
That child socket (and the VMBUS channel opened for it) is never accepted
or cleaned up and leaks.
The virtio transport guards exactly this case in
virtio_transport_recv_listen(); hv_sock lacks the equivalent guard.
hv_sock holds lock_sock(sk) across hvs_open_connection(), so the check is
race-free.
Signed-off-by: Bartłomiej Dmitruk <bartlomiej.dmitruk@isec.pl>
---
diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -324,6 +324,14 @@
if (conn_from_host) {
if (sk_acceptq_is_full(sk))
+ goto out;
+
+ /* __vsock_release() may have already flushed the accept queue
+ * and set sk_shutdown = SHUTDOWN_MASK while leaving sk_state ==
+ * TCP_LISTEN. Enqueuing a child now would leak the child socket
+ * and its VMBUS channel. Mirror virtio_transport_recv_listen().
+ */
+ if (sk->sk_shutdown == SHUTDOWN_MASK)
goto out;
new = vsock_create_connected(sk);
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released
2026-09-17 22:01 [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released Bartłomiej Dmitruk
@ 2026-09-18 13:17 ` Stefano Garzarella
2026-09-21 23:17 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: Stefano Garzarella @ 2026-09-18 13:17 UTC (permalink / raw)
To: Bartłomiej Dmitruk
Cc: Dexuan Cui, Wei Liu, Haiyang Zhang, K . Y . Srinivasan,
linux-hyperv, netdev, Michael S . Tsirkin
On Fri, Sep 18, 2026 at 12:01:54AM +0200, Bartłomiej Dmitruk wrote:
>hvs_open_connection() only checks sk_state == TCP_LISTEN and
>sk_acceptq_is_full() before creating a child and calling
>vsock_enqueue_accept(). __vsock_release() sets sk->sk_shutdown =
>SHUTDOWN_MASK and flushes the accept queue while leaving sk_state ==
>TCP_LISTEN, so a host channel offer that races close() of the listener
>passes both checks and enqueues a child onto the already-flushed queue.
>That child socket (and the VMBUS channel opened for it) is never accepted
>or cleaned up and leaks.
>
>The virtio transport guards exactly this case in
>virtio_transport_recv_listen(); hv_sock lacks the equivalent guard.
>hv_sock holds lock_sock(sk) across hvs_open_connection(), so the check is
>race-free.
>
Fixes tag missing.
>Signed-off-by: Bartłomiej Dmitruk <bartlomiej.dmitruk@isec.pl>
>---
>diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
>--- a/net/vmw_vsock/hyperv_transport.c
>+++ b/net/vmw_vsock/hyperv_transport.c
>@@ -324,6 +324,14 @@
>
> if (conn_from_host) {
> if (sk_acceptq_is_full(sk))
>+ goto out;
>+
>+ /* __vsock_release() may have already flushed the accept queue
>+ * and set sk_shutdown = SHUTDOWN_MASK while leaving sk_state ==
>+ * TCP_LISTEN. Enqueuing a child now would leak the child socket
>+ * and its VMBUS channel. Mirror virtio_transport_recv_listen().
>+ */
>+ if (sk->sk_shutdown == SHUTDOWN_MASK)
> goto out;
Should we do this check also if conn_from_host is false?
If it's the case, I guess we can add that check in the checks we do
after lock_sock().
Thanks,
Stefano
>
> new = vsock_create_connected(sk);
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released
2026-09-17 22:01 [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released Bartłomiej Dmitruk
2026-09-18 13:17 ` Stefano Garzarella
@ 2026-09-21 23:17 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-09-21 23:17 UTC (permalink / raw)
To: bartlomiej.dmitruk
Cc: decui, wei.liu, haiyangz, kys, linux-hyperv, netdev, sgarzare,
mst, kuba
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 1 · High: 0 (1) · Medium: 0 · Low: 1
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Critical] The new early-exit added to hvs_open_connection() jumps to
the `out:` label, which performs `sock_put(sk)`…
- [Low] The commit fixes a real, user-visible socket and VMBUS-channel
leak in hvs_open_connection() (net/vmw_vsock/hyperv_transport.c:334)…
Pre-existing issues:
- [High] hvs_open_connection() (net/vmw_vsock/hyperv_transport.c): the
new released-listener guard `if (sk->sk_shutdown == SHUTDOWN_MASK)…
--
vsock/hv_sock: reject incoming connections when the listener is being released
This adds a check for sk_shutdown == SHUTDOWN_MASK in
hvs_open_connection() so that a host channel offer racing close() of an
hv_sock listener does not create a child socket and enqueue it onto an
accept queue that __vsock_release() has already flushed. The stated goal
is to stop the child socket and its VMBUS channel from leaking.
No Link: tags are present in the commit header.
A few questions about the change below.
[Severity: Low]
Should this carry a Fixes: tag?
The unguarded child creation in hvs_open_connection(), and the release
path that sets sk_shutdown = SHUTDOWN_MASK while leaving sk_state ==
TCP_LISTEN, both date back to the introduction of the transport, so the
trailer would be:
Fixes: ae0078fcf0a5 ("hv_sock: implements Hyper-V transport for Virtual Sockets (AF_VSOCK)")
Without it, stable tooling will not pick up what is otherwise a real leak
fix.
> diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
> index 855b15971c359..19e487ac27263 100644
> --- a/net/vmw_vsock/hyperv_transport.c
> +++ b/net/vmw_vsock/hyperv_transport.c
> @@ -326,6 +326,14 @@ static void hvs_open_connection(struct vmbus_channel *chan)
> if (sk_acceptq_is_full(sk))
> goto out;
>
> + /* __vsock_release() may have already flushed the accept queue
> + * and set sk_shutdown = SHUTDOWN_MASK while leaving sk_state ==
> + * TCP_LISTEN. Enqueuing a child now would leak the child socket
> + * and its VMBUS channel. Mirror virtio_transport_recv_listen().
> + */
> + if (sk->sk_shutdown == SHUTDOWN_MASK)
> + goto out;
> +
[Severity: Critical]
Can this new goto out lead to a use-after-free of the listener?
The out: epilogue in hvs_open_connection() drops the reference before it
unlocks:
net/vmw_vsock/hyperv_transport.c:hvs_open_connection() {
...
out:
/* Release refcnt obtained when we called vsock_find_bound_socket() */
sock_put(sk);
release_sock(sk);
}
In the exact race this patch targets, is the vsock_find_bound_socket()
reference the last one left? The racing close() drops everything else:
net/vmw_vsock/af_vsock.c:__vsock_release() {
sock_set_flag(sk, SOCK_DEAD);
if (vsk->transport)
vsk->transport->release(vsk); /* hvs_release() ->
* vsock_remove_sock() ->
* vsock_remove_bound()
*/
sock_orphan(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
...
while ((pending = vsock_dequeue_accept(sk)) != NULL) {
__vsock_release(pending, SINGLE_DEPTH_NESTING);
sock_put(pending);
}
release_sock(sk);
sock_put(sk);
}
vsock_dequeue_accept() also does sock_put(listener) for each queued child,
and hvs_close_lock_held() returns true immediately for TCP_LISTEN without
touching sk_state, so once __vsock_release() finishes the listener is only
kept alive by the lookup reference held by hvs_open_connection().
hvs_open_connection() then takes the lock, sees sk_state == TCP_LISTEN,
matches the new sk_shutdown == SHUTDOWN_MASK test, and goes to out:, where
sock_put() drops the count to zero. vsock_proto sets neither SOCK_RCU_FREE
nor SLAB_TYPESAFE_BY_RCU, so sk_free() -> __sk_destruct() ->
kmem_cache_free() runs synchronously and the following release_sock(sk)
touches sk->sk_lock.slock, the backlog and the wait queue on freed memory.
Before this change the same race normally continued into
vsock_enqueue_accept(), which does sock_hold(listener), so the leak being
fixed here is what kept the socket alive past that sock_put(). Should the
epilogue be reordered to release_sock(sk) followed by sock_put(sk) as part
of this fix?
[Severity: High]
This isn't a bug introduced by this patch, but should the new test be
hoisted above the if (conn_from_host) block so it also covers the
guest-initiated arm?
As placed, the check only runs for conn_from_host. For the
!conn_from_host case hvs_open_connection() accepts sk_state ==
TCP_SYN_SENT, and hvs_close_lock_held() leaves sk_state alone for that
state too, so a socket that __vsock_release() has already torn down still
looks connectable while sock_orphan() has cleared sk->sk_socket. The else
branch then does:
net/vmw_vsock/hyperv_transport.c:hvs_open_connection() {
...
} else {
sk->sk_state = TCP_ESTABLISHED;
sk->sk_socket->state = SS_CONNECTED;
vsock_insert_connected(vsock_sk(sk));
}
...
}
which writes through a NULL sk_socket, and inserts an already released
socket into the connected table.
Core vsock already treats that combination as a state where sk_socket must
not be dereferenced:
net/vmw_vsock/af_vsock.c:vsock_connect_timeout() {
...
if (sk->sk_state == TCP_SYN_SENT &&
(sk->sk_shutdown != SHUTDOWN_MASK)) {
sk->sk_state = TCP_CLOSE;
sk->sk_socket->state = SS_UNCONNECTED;
...
}
Moving the new check up to just after the sk_state tests would cover both
arms.
> new = vsock_create_connected(sk);
> if (!new)
> goto out;
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260917220156.56015-1-bartlomiej.dmitruk%40isec.pl
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-21 23:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-17 22:01 [PATCH] vsock/hv_sock: reject incoming connections when the listener is being released Bartłomiej Dmitruk
2026-09-18 13:17 ` Stefano Garzarella
2026-09-21 23:17 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox