From 8c4b526ef48b28f7f0c489ba277c5c033949f28f Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 2 Feb 2026 04:16:05 +0000 Subject: [PATCH] fix(container): prevent data race in State.Get and State.All (#238) Return copies of Container structs instead of pointers to the map entries. This prevents data races when containers are modified concurrently by waitForExit and Stop. Fixes #76 Co-authored-by: Claude Opus 4.5 --- pkg/container/state.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/container/state.go b/pkg/container/state.go index 53ab1e2a..b8d98b97 100644 --- a/pkg/container/state.go +++ b/pkg/container/state.go @@ -99,13 +99,19 @@ func (s *State) Add(c *Container) error { return s.SaveState() } -// Get retrieves a container by ID. +// Get retrieves a copy of a container by ID. +// Returns a copy to prevent data races when the container is modified. func (s *State) Get(id string) (*Container, bool) { s.mu.RLock() defer s.mu.RUnlock() c, ok := s.Containers[id] - return c, ok + if !ok { + return nil, false + } + // Return a copy to prevent data races + copy := *c + return ©, true } // Update updates a container in the state and persists it. @@ -126,14 +132,16 @@ func (s *State) Remove(id string) error { return s.SaveState() } -// All returns all containers in the state. +// All returns copies of all containers in the state. +// Returns copies to prevent data races when containers are modified. func (s *State) All() []*Container { s.mu.RLock() defer s.mu.RUnlock() containers := make([]*Container, 0, len(s.Containers)) for _, c := range s.Containers { - containers = append(containers, c) + copy := *c + containers = append(containers, ©) } return containers }