CRITICAL: Global State Race Conditions #2

Open
opened 2026-02-16 21:58:19 +02:00 by Koko210 · 0 comments
Owner

What the Problem Is

Multiple global variables in globals.py are accessed and modified by different async functions without any locking mechanism. In async code, multiple coroutines can be running "at the same time" (awaiting different I/O operations), and they can read and write to shared variables simultaneously, causing data corruption.

Where It Occurs

  • bot/globals.py#L18-19 - conversation_history (defaultdict)
  • bot/globals.py#L52-54 - BIPOLAR_WEBHOOKS (dict)
  • bot/globals.py#L58-62 - BIPOLAR_ARGUMENT_IN_PROGRESS (dict)
  • bot/globals.py#L108-111 - VOICE_SESSION_ACTIVE (bool)
  • bot/globals.py#L108-111 - TEXT_MESSAGE_QUEUE (list)

Why This Is a Problem

In async Python, when you await, the function yields control to the event loop. Another coroutine can run and modify the same variable.

What Can Go Wrong

Scenario 1: Voice Session State Corruption

  1. Voice session starts, VOICE_SESSION_ACTIVE set to True
  2. Two different coroutines both check if voice is active at the same time
  3. Both see True, so both try to pause text channels
  4. First coroutine sets TEXT_MESSAGE_QUEUE = [] to start fresh
  5. Second coroutine also sets TEXT_MESSAGE_QUEUE = [] (first one lost)
  6. Messages that should be queued are lost forever

Scenario 2: Conversation History Loss

  1. Two messages arrive from the same user simultaneously
  2. Both coroutines try to append to conversation_history[user_id]
  3. The deque has maxlen=5 (automatically removes oldest when full)
  4. Both coroutines might read the same state before either writes
  5. One update overwrites the other
  6. One message's context is lost from history

Proposed Fix

Create locks for each shared resource in globals.py:

import asyncio

# Create locks for each shared resource
voice_session_lock = asyncio.Lock()
text_queue_lock = asyncio.Lock()
conversation_lock = asyncio.Lock()
bipolar_state_lock = asyncio.Lock()

# When modifying shared state:
async def _pause_text_channels(self):
    """Queue text messages instead of processing during voice session"""
    async with voice_session_lock:
        async with text_queue_lock:
            globals.VOICE_SESSION_ACTIVE = True
            globals.TEXT_MESSAGE_QUEUE = []
    logger.info("✓ Text channels paused (messages will be queued)")

Severity

CRITICAL - Data corruption, lost messages, inconsistent state, unpredictable behavior.

Files Affected

5 files including globals.py, voice_manager.py, autonomous.py, bipolar_mode.py

## What the Problem Is Multiple global variables in `globals.py` are accessed and modified by different async functions without any locking mechanism. In async code, multiple coroutines can be running "at the same time" (awaiting different I/O operations), and they can read and write to shared variables simultaneously, causing data corruption. ## Where It Occurs - `bot/globals.py#L18-19` - `conversation_history` (defaultdict) - `bot/globals.py#L52-54` - `BIPOLAR_WEBHOOKS` (dict) - `bot/globals.py#L58-62` - `BIPOLAR_ARGUMENT_IN_PROGRESS` (dict) - `bot/globals.py#L108-111` - `VOICE_SESSION_ACTIVE` (bool) - `bot/globals.py#L108-111` - `TEXT_MESSAGE_QUEUE` (list) ## Why This Is a Problem In async Python, when you `await`, the function yields control to the event loop. Another coroutine can run and modify the same variable. ## What Can Go Wrong ### Scenario 1: Voice Session State Corruption 1. Voice session starts, `VOICE_SESSION_ACTIVE` set to `True` 2. Two different coroutines both check if voice is active at the same time 3. Both see `True`, so both try to pause text channels 4. First coroutine sets `TEXT_MESSAGE_QUEUE = []` to start fresh 5. Second coroutine also sets `TEXT_MESSAGE_QUEUE = []` (first one lost) 6. Messages that should be queued are lost forever ### Scenario 2: Conversation History Loss 1. Two messages arrive from the same user simultaneously 2. Both coroutines try to append to `conversation_history[user_id]` 3. The deque has `maxlen=5` (automatically removes oldest when full) 4. Both coroutines might read the same state before either writes 5. One update overwrites the other 6. One message's context is lost from history ## Proposed Fix Create locks for each shared resource in globals.py: ```python import asyncio # Create locks for each shared resource voice_session_lock = asyncio.Lock() text_queue_lock = asyncio.Lock() conversation_lock = asyncio.Lock() bipolar_state_lock = asyncio.Lock() # When modifying shared state: async def _pause_text_channels(self): """Queue text messages instead of processing during voice session""" async with voice_session_lock: async with text_queue_lock: globals.VOICE_SESSION_ACTIVE = True globals.TEXT_MESSAGE_QUEUE = [] logger.info("✓ Text channels paused (messages will be queued)") ``` ## Severity **CRITICAL** - Data corruption, lost messages, inconsistent state, unpredictable behavior. ## Files Affected 5 files including globals.py, voice_manager.py, autonomous.py, bipolar_mode.py
Koko210 reopened this issue 2026-02-16 22:17:02 +02:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Koko210/miku-discord#2