From bc21da9cb3b67139f115915d1e3e87954d6c120b Mon Sep 17 00:00:00 2001 From: henrygd Date: Wed, 2 Sep 2026 20:31:38 -0400 Subject: [PATCH] fix(agent): prevent possible deadlock when stopping SSH server (#2280) --- agent/connection_manager.go | 4 +++- agent/connection_manager_test.go | 6 ++++++ agent/server.go | 1 - agent/server_test.go | 22 ++++++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/agent/connection_manager.go b/agent/connection_manager.go index f9854f2b..d9b16d24 100644 --- a/agent/connection_manager.go +++ b/agent/connection_manager.go @@ -155,7 +155,9 @@ func (c *ConnectionManager) handleEvent(event ConnectionEvent) { case WebSocketConnect: c.handleStateChange(WebSocketConnected) case SSHConnect: - c.handleStateChange(SSHConnected) + if c.State == Disconnected { + c.handleStateChange(SSHConnected) + } case WebSocketDisconnect: if c.State == WebSocketConnected { c.handleStateChange(Disconnected) diff --git a/agent/connection_manager_test.go b/agent/connection_manager_test.go index b78fc5fc..ef9222bd 100644 --- a/agent/connection_manager_test.go +++ b/agent/connection_manager_test.go @@ -114,6 +114,12 @@ func TestConnectionManager_EventHandling(t *testing.T) { event: SSHConnect, expectedState: SSHConnected, }, + { + name: "SSH connect from WebSocket connected (no change)", + initialState: WebSocketConnected, + event: SSHConnect, + expectedState: WebSocketConnected, + }, { name: "WebSocket disconnect from connected", initialState: WebSocketConnected, diff --git a/agent/server.go b/agent/server.go index 4a86fd3a..2c209fa0 100644 --- a/agent/server.go +++ b/agent/server.go @@ -265,6 +265,5 @@ func (a *Agent) StopServer() error { slog.Info("Stopping SSH server") _ = a.server.Close() a.server = nil - a.connectionManager.eventChan <- SSHDisconnect return nil } diff --git a/agent/server_test.go b/agent/server_test.go index 274cb45c..d169234d 100644 --- a/agent/server_test.go +++ b/agent/server_test.go @@ -198,6 +198,28 @@ func TestStartServerDisableSSH(t *testing.T) { assert.Contains(t, err.Error(), "SSH disabled") } +func TestStopServerDoesNotBlockWhenEventQueueFull(t *testing.T) { + agent := createTestAgent(t) + agent.server = &ssh.Server{} + agent.connectionManager.eventChan = make(chan ConnectionEvent, 1) + agent.connectionManager.eventChan <- WebSocketConnect + + done := make(chan error, 1) + go func() { + done <- agent.StopServer() + }() + + select { + case err := <-done: + require.NoError(t, err) + case <-time.After(time.Second): + t.Fatal("StopServer blocked on the connection event queue") + } + + assert.Nil(t, agent.server) + assert.Equal(t, WebSocketConnect, <-agent.connectionManager.eventChan) +} + ///////////////////////////////////////////////////////////////// //////////////////// ParseKeys Tests //////////////////////////// /////////////////////////////////////////////////////////////////