fix(whatsapp_native): close TOCTOU race between eventHandler and Stop
Move the stopping check and wg.Add(1) inside reconnectMu in eventHandler, and set the stopping flag under the same lock in Stop(). This makes the two operations atomic with respect to each other, preventing the race where: 1. eventHandler checks stopping (false) 2. Stop() sets stopping=true and enters wg.Wait() (wg is 0) 3. eventHandler calls wg.Add(1) → panic or goroutine leak
This commit is contained in:
parent
9b80fdf885
commit
fc28c2660a
1 changed files with 15 additions and 6 deletions
|
|
@ -183,8 +183,14 @@ func (c *WhatsAppNativeChannel) Start(ctx context.Context) error {
|
|||
func (c *WhatsAppNativeChannel) Stop(ctx context.Context) error {
|
||||
logger.InfoC("whatsapp", "Stopping WhatsApp native channel")
|
||||
|
||||
// Mark as stopping so no new goroutines are spawned via eventHandler.
|
||||
// Mark as stopping under reconnectMu so the flag is visible to
|
||||
// eventHandler atomically with respect to its wg.Add(1) call.
|
||||
// This closes the TOCTOU window where eventHandler could check
|
||||
// stopping (false), then Stop sets it true + enters wg.Wait,
|
||||
// then eventHandler calls wg.Add(1) — causing a panic.
|
||||
c.reconnectMu.Lock()
|
||||
c.stopping.Store(true)
|
||||
c.reconnectMu.Unlock()
|
||||
|
||||
if c.runCancel != nil {
|
||||
c.runCancel()
|
||||
|
|
@ -236,18 +242,21 @@ func (c *WhatsAppNativeChannel) eventHandler(evt any) {
|
|||
c.handleIncoming(evt.(*events.Message))
|
||||
case *events.Disconnected:
|
||||
logger.InfoCF("whatsapp", "WhatsApp disconnected, will attempt reconnection", nil)
|
||||
// Prevent new goroutines once Stop() has begun.
|
||||
if c.stopping.Load() {
|
||||
return
|
||||
}
|
||||
c.reconnectMu.Lock()
|
||||
if c.reconnecting {
|
||||
c.reconnectMu.Unlock()
|
||||
return
|
||||
}
|
||||
// Check stopping while holding the lock so the check and wg.Add
|
||||
// are atomic with respect to Stop() setting the flag + calling
|
||||
// wg.Wait(). This prevents the TOCTOU race.
|
||||
if c.stopping.Load() {
|
||||
c.reconnectMu.Unlock()
|
||||
return
|
||||
}
|
||||
c.reconnecting = true
|
||||
c.reconnectMu.Unlock()
|
||||
c.wg.Add(1)
|
||||
c.reconnectMu.Unlock()
|
||||
go func() {
|
||||
defer c.wg.Done()
|
||||
c.reconnectWithBackoff()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue