race-condition · git:20261016.df7fb32 · 2026-10-16 · sha256 07c1941850410cd5
race-condition git:20261016.df7fb32A
Immutable. This exact content never changes and is served at /api/v1/blob/07c1941850410cd5.
--- name: race-condition description: Classify race condition evidence — TOCTOU (access→fopen), shared-state (lock→unlock→mutate), and classic shared-variable data races in pthread code. Maps to CWE-362. license: MIT compatibility: opencode,claude code,DSH metadata: cwe: CWE-362 severity: HIGH domain: concurrency --- ## Race Condition Analysis (CWE-362) ### Evidence Patterns #### TOCTOU: Filesystem (CWE-362) - **RACE_CONDITION event** with category `toctou` - Pattern: `access(path, ...)` check followed by `fopen(path, ...)` / `open(path, ...)` - Time-of-check to time-of-use window allows symlink attack or file swap #### TOCTOU: Shared State (CWE-362) - **RACE_CONDITION event** with category `toctou_shared_state` - Pattern: `mutex_lock` → `check` → `mutex_unlock` → `mutate` - Check and mutation not atomic; another thread can change state between unlock and mutate #### Classic Data Race (CWE-362) - **RACE_CONDITION event** with category `shared_data_race` - Pattern: a file-scope variable accessed by two or more `pthread_create` thread functions, with at least one write, and no `pthread_mutex_lock`/`unlock` scope around the access - Examples: `g_shared_counter++` in two threads, writer/reader pairs on `g_flag`/`g_data` - The event carries `thread_functions`, `thread_instances` and `write_line` properties Evidence file fragments (per category): - `toctou` — **toctou**: the site; **check_call** / **use_call** / **shared_arg**: the check call (e.g. `access`), the later use call (e.g. `fopen`), and the argument shared between them - `toctou_shared_state` — **shared_state**: the post-unlock write; **lock_scope**: the guarding mutex and its lock/unlock lines - `shared_data_race` — **data_race**: the access; **thread_functions** / **thread_instances** / **write_line**: the racing threads and the conflicting write - all — **call_path**: the function is reachable from an entry point ### Safe Patterns (P0 Exclusion) | Safe Pattern | Why Safe | |---------------|----------| | `fopen(path, "r")` directly, check result | No check-then-use window | | `open(path, O_RDONLY | O_NOFOLLOW)` | Rejects symlinks atomically | | `fstat(fd, &st)` after `open` | Check on the fd, not the path | | Lock held through both check and mutation | Atomic check-then-act | ### Classification Rules | Condition | Classification | |-----------|---------------| | `access()` + `fopen()` on same path, no atomicity | **confirmed** | | `access()` + `open()` with `O_NOFOLLOW` | **false-positive** | | Mutate a shared/global variable after releasing a lock | **dismissed** (a TOCTOU only if the same variable was READ inside the lock — verify the check-then-act pair) | | Lock held through check + mutate | **false-positive** | | `access()` + `fopen()` in same function, path is local | **dismissed** (may be safe if path not attacker-controlled) | | Check-then-act with no shared state | **false-positive** | | Shared variable, >= 2 pthread threads, >= 1 write, no lock | **confirmed** | | Shared variable, all accesses inside lock scope | **false-positive** | | Shared variable accessed by only one thread function | **false-positive** (no concurrent access) | ### Fix Suggestions - Replace `access()` + `fopen()` with direct `fopen()` and error check - Use `open()` with `O_NOFOLLOW` to reject symlinks - Hold the mutex through both check and mutation (don't unlock between) - Use `fstat()` on the fd instead of `stat()` on the path - For file locking, use `flock()` or `fcntl(F_SETLK)` for atomic lock+check - Consider single-threaded design for simple cases ### Severity Matrix `status` and `severity` are independent: `status` = evidence verdict (confirmed/dismissed), `severity` = impact (low/medium/high/critical). `metadata.severity` is the type default, not a ceiling. The verdict is binary (confirmed/dismissed); dismissed → `low`. | Shape | Severity | |-------|----------| | Shared-data race (≥2 threads, ≥1 write, no lock) → memory corruption / UB | HIGH | | TOCTOU filesystem (`access`→`open`/`fopen`) on an attacker-influenced path | HIGH (CRITICAL if it crosses a privilege boundary) | | TOCTOU shared-state (check-then-act split across unlock) | MEDIUM (dismissed) | | `access()`+`fopen()` where the path is local/trusted | MEDIUM (dismissed) |