-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Description
The s.clients map in the Server structure appears to be accessed concurrently without proper synchronization, which may lead to race conditions.
-
Example 1: Access in
findClientBySessionID// returns the Client by the Session ID func (s *Server) findClientBySessionID(sessionID []byte) (*Client, bool) { for _, client := range s.clients { if bytes.Equal(client.sessionID, sessionID) { return client, true } } return nil, false }
In this method,
s.clientsis iterated over, potentially while other parts of the code modify it. -
Example 2: Modification in Garbage Collection
case <-s.garbageCollectionTicker.C: for _, c := range s.clients { if c.lastHeartbeat != nil && time.Now().After(c.lastHeartbeat.Add(s.heartbeatExpiration)) { delete(s.clients, c.ID) delete(s.sessions, fmt.Sprintf("%s_%d", c.addr.IP.String(), c.addr.Port)) } } }
The garbage collection routine iterates over and modifies
s.clientsconcurrently, potentially leading to a panic due to a concurrent map read and write.
Problem
The s.clients map is not thread-safe. While one goroutine (e.g., the garbage collector) might be deleting clients, another goroutine (e.g., findClientBySessionID) could be iterating or reading from it, leading to undefined behavior or a runtime panic.
Suggestions
-
Use
sync.Map
Replaces.clientswith async.Mapunder a type wrapper. This will ensure thread-safe operations for both reading and writing. -
Introduce a Mutex
Use async.Mutexorsync.RWMutexto explicitly lock thes.clientsmap during reads and writes. This approach ensures safety and maintains the existing map structure but could introduce performance overhead depending on the frequency of access.