# 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. **Stack:** Go - Prevalence: 中 覆盖 3 种语言 - Impact: 高 4 条严重级别为高的规则 - Prevention: 已记录 6 个修复示例 **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 基于 4 条 Shoulder 检测规则的 Race Condition 预防策略。 ### 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 ## 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 - [MEDIUM] Shared data accessed without proper synchronization ## Consequences - 修改应用程序数据 - 拒绝服务 (DoS) - 执行未授权代码 - 绕过保护机制 ## Mitigations - 使用合适的同步原语,如锁、互斥量或信号量 - 尽量减少临界区中的代码量 - 在可用时使用线程安全的数据结构 ## 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() ```