# Concurrent Execution Using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362) The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently. - Prevalence: Medium 3 languages covered - Impact: High 4 high-severity rules - Prevention: Documented 6 fix examples **OWASP:** Insecure Design (A04:2021-Insecure Design) - #4 ## Description This can have security implications when the expected synchronization is in security-critical code, such as recording whether a user is authenticated or modifying important state information that should not be influenced by an outsider. ## Prevention Prevention strategies for Race Condition based on 6 Shoulder detection rules. ### Key Practices - Use data races, lost data, or panics - Use race conditions - Use of sync ### Go Protect concurrent slice access with mutex or use channels to collect results Use thread-safe accessor methods or sync.RWMutex for concurrent map access Protect shared state with sync.Mutex, atomic operations, or sync.Map ### Node.js Use database transactions with row-level locking for atomic read-modify-write operations ### Python Use locks for shared data and atomic operations for file access ## Warning Signs - [HIGH] Concurrent slice access without synchronization causes data races. append() is NOT atomic - multiple goroutines appendin - [HIGH] Direct access to map fields can cause race conditions in concurrent code. Maps in Go are not thread-safe, and concurrent - [HIGH] Improper WaitGroup usage can cause: - Race conditions (Add inside goroutine) - Panics (negative counter from Done miscou - [HIGH] Race condition at ... - check and act are not atomic - [HIGH] time-of-check to time-of-use (TOCTOU) vulnerabilities where the state can change between checking a - [MEDIUM] Shared data accessed without proper synchronization - [MEDIUM] potential race conditions in concurrent Python code ## Consequences - Modify Application Data - DoS - Execute Unauthorized Code - Bypass Protection Mechanism ## Mitigations - Use proper synchronization primitives like locks, mutexes, or semaphores - Minimize the amount of code inside critical sections - Use thread-safe data structures when available ## Detection - Total rules: 6 - Languages: go, javascript, typescript, python ## Rules by Language ### Go (4 rules) - **Concurrent Slice Access** [HIGH]: Concurrent access to slices (especially append) without synchronization can cause data races, lost data, or panics. Slices in Go are not thread-safe. - Remediation: Option 1: Protect with mutex ```go var mu sync.Mutex var results []string go func() { mu.Lock() results = append(results, item) mu.Unlock() }() ``` Option 2: Use channels (preferred for Go) ```go resultsCh := make(chan string, n) for i := 0; i < n; i++ { go func() { resultsCh <- processItem() }() } // Collect safely var results []string for i := 0; i < n; i++ { results = append(results, <-resultsCh) } ``` Option 3: Pre-allocate with unique indices ```go results := make([]string, n) // Pre-allocate for i := 0; i < n; i++ { i := i // Capture loop variable go func() { results[i] = process() // Safe: unique index per goroutine }() } ``` - **Direct Map Access on Thread-Safe Struct** [HIGH]: Direct access to map fields on structs that provide thread-safe accessor methods can cause race conditions. Use the provided accessor methods instead. - Remediation: Use thread-safe accessor methods if available: ```go // Instead of: value := obj.MapField["key"] value, ok := obj.GetAttr("key") // Instead of: obj.MapField["key"] = value obj.SetAttr("key", value) ``` Or protect with mutex: ```go mu.RLock() value := obj.MapField["key"] mu.RUnlock() ``` - **Potential Race Condition** [MEDIUM]: Shared data accessed from goroutines without synchronization. - Remediation: Protect shared data with sync.Mutex or use sync.Map for concurrent map access. ```go var mu sync.Mutex var counter int func increment() { mu.Lock() defer mu.Unlock() counter++ } ``` Learn more: https://shoulder.dev/learn/go/cwe-362/race-condition - **WaitGroup Misuse** [HIGH]: Improper use of sync.WaitGroup can cause race conditions, panics, or deadlocks. Common issues include calling Add() inside goroutines and Done() count mismatches. - Remediation: 1. Always Add() BEFORE starting goroutine: ```go wg.Add(1) go func() { defer wg.Done() // work }() ``` 2. Always use defer wg.Done() to ensure it runs: ```go go func() { defer wg.Done() // Runs even if panic occurs work() }() ``` 3. Consider using errgroup for better error handling: ```go g, ctx := errgroup.WithContext(ctx) g.Go(func() error { return work() }) err := g.Wait() ``` ### Javascript (1 rules) - **Race Condition in Concurrent Operations** [HIGH]: Detects time-of-check to time-of-use (TOCTOU) vulnerabilities where the state can change between checking a condition and acting on it. Common race conditions include: - Check balance, then deduct (balance can change in between) - Check inventory, then create order (stock can be sold out) - Check permissions, then perform action (permissions can change) - File existence check, then read/write (file can be modified) - Remediation: Use database transactions for atomic operations: ```javascript // ✅ SAFE - Atomic operation with transaction const transaction = await db.transaction(); try { const account = await Account.findOne({ where: { userId }, lock: transaction.LOCK.UPDATE, transaction }); if (account.balance < amount) { await transaction.rollback(); throw new Error('Insufficient funds'); } await account.update( { balance: account.balance - amount }, { transaction } ); await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; } ``` ### Typescript (1 rules) - **Race Condition in Concurrent Operations** [HIGH]: Detects time-of-check to time-of-use (TOCTOU) vulnerabilities where the state can change between checking a condition and acting on it. Common race conditions include: - Check balance, then deduct (balance can change in between) - Check inventory, then create order (stock can be sold out) - Check permissions, then perform action (permissions can change) - File existence check, then read/write (file can be modified) - Remediation: Use database transactions for atomic operations: ```javascript // ✅ SAFE - Atomic operation with transaction const transaction = await db.transaction(); try { const account = await Account.findOne({ where: { userId }, lock: transaction.LOCK.UPDATE, transaction }); if (account.balance < amount) { await transaction.rollback(); throw new Error('Insufficient funds'); } await account.update( { balance: account.balance - amount }, { transaction } ); await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; } ``` ### Python (1 rules) - **Potential Race Condition** [MEDIUM]: Detects potential race conditions in concurrent Python code. Common race condition patterns: 1. Global variables accessed from threads without locking 2. TOCTOU (Time-of-check Time-of-use) file operations 3. Shared data structures modified in threads without synchronization 4. Check-then-act patterns without atomicity Python's GIL doesn't prevent all race conditions, especially with I/O operations and multi-process code. - Remediation: Use locks for shared data and atomic operations for file access. ```python import threading import os # Use locks for shared data lock = threading.Lock() with lock: counter += 1 # Atomic file creation (no TOCTOU) try: fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) os.close(fd) except FileExistsError: pass # Or use makedirs with exist_ok os.makedirs(path, exist_ok=True) ``` Learn more: https://shoulder.dev/learn/python/cwe-362/race-condition