git:20260913.4c35c60 to git:20260913.46c999d

15 added, 0 removed. Audit A to A.

---
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
### 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 | **suspected** (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 | **suspected** (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/suspected/dismissed), `severity` = impact (low/medium/high/critical).
+ `metadata.severity` is the type default, not a ceiling. Suspected findings are
+ capped one notch below their confirmed twin (evidence discount), never below
+ `low`; 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 (suspected) |
+ | `access()`+`fopen()` where the path is local/trusted | MEDIUM (suspected) |
+