From dc7f22acfba4e8166d2c09984cf5edeb9ceeb513 Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 2 Feb 2026 04:13:24 +0000 Subject: [PATCH] fix(container): prevent data race in State.Get and State.All 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 }