From d9622dfc7a3b452fdacedb6a9017121ef1d15a7a Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Tue, 19 May 2026 15:26:33 +0200 Subject: [PATCH] fix: deadlock in session rename (lock ordering inversion) --- server.go | 5 ++++- store/session.go | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index b3d0732..1da87a6 100644 --- a/server.go +++ b/server.go @@ -283,7 +283,10 @@ func New(sink *olog.Sink, opts ...ServerOption) *Server { s.AddGroup("agent", newID) oldPrefix := "/s/" + oldID newPrefix := "/s/" + newID - for _, c := range s.conns { + s.mu.RLock() + conns := s.conns + s.mu.RUnlock() + for _, c := range conns { c.mu.Lock() for _, f := range c.fids { if f.path == oldPrefix || strings.HasPrefix(f.path, oldPrefix+"/") { diff --git a/store/session.go b/store/session.go index 87a0a61..4f47793 100644 --- a/store/session.go +++ b/store/session.go @@ -465,27 +465,34 @@ func (s *SessionStore) createSession(args []string) error { func (s *SessionStore) renameSession(oldID, newID string) error { s.mu.Lock() - defer s.mu.Unlock() sess, ok := s.sessions[oldID] if !ok { + s.mu.Unlock() return fmt.Errorf("session not found: %s", oldID) } if _, exists := s.sessions[newID]; exists { + s.mu.Unlock() return fmt.Errorf("session already exists: %s", newID) } if sess.Core.IsRunning() { + s.mu.Unlock() return fmt.Errorf("cannot rename while agent is running") } if err := sess.Core.SetSessionID(newID); err != nil { + s.mu.Unlock() return err } sess.id = newID s.sessions[newID] = sess delete(s.sessions, oldID) + s.mu.Unlock() + // OnRename must be called without holding s.mu to avoid deadlock + // with connection handlers that hold connState.mu and call into + // SessionStore (e.g. SessionByUname during Tattach). if s.cfg.OnRename != nil { s.cfg.OnRename(oldID, newID) }