Overview
A race condition appears when correct behavior depends on the order or timing of concurrent operations, but the application does not enforce that order safely. In web applications, this usually means two or more requests reach the same shared state at almost the same time and the application makes decisions using stale or inconsistent data. These notes cover the concurrency model, TOCTOU bugs, practical business-logic cases, and how to think about testing race conditions in real applications.
Programs, Processes, and Threads
A program is static code. A process is that code running. A thread is a lightweight execution unit inside a process.
This matters because many web servers handle multiple requests at once by using multiple workers, threads, or asynchronous execution. Once more than one request can touch the same data at the same time, race-condition risk appears.
Why Web Apps Are Vulnerable
Web applications are full of shared state:
- account balances,
- inventory counts,
- one-time coupon state,
- password reset tokens,
- authentication session transitions,
- database rows representing “available” resources.
If two requests read that state before either one writes back the new value, both may act as if they were first.
TOCTOU
The most important race-condition pattern is Time of Check to Time of Use, usually shortened to TOCTOU.
The application checks whether an action is allowed, then later performs the action. If another request changes the underlying state in between, the original decision may no longer be valid.
Simple Analogy
Two restaurant hosts both see table 17 as free. Each one promises it to a different caller before the “reserved” marker is actually placed on the table. The check was correct when it happened, but the later use of that information was no longer safe.
That is the same logic bug web applications make with funds, coupons, seats, quotas, and one-time actions.
Banking Example
Suppose an account has $75 and two concurrent requests each attempt to withdraw $50.
- Request A checks the balance and sees
$75.
- Before A updates the record, Request B also checks the balance and sees
$75.
- Both withdrawals proceed, even though only one should succeed.
That is a classic TOCTOU failure: the validation step and the state update were not atomic.
Coupon Example
Coupon redemption is another common race target. The application may follow a flow like this:
- check whether the coupon is valid,
- check whether the user has already used it,
- apply the discount,
- mark the coupon as consumed.
If two requests hit the “apply” action before the coupon is marked used, the discount may be applied more than once.
Common Race Targets in Web Apps
- balance transfer and withdrawal logic,
- inventory reservation and limited-stock purchases,
- gift cards and coupon redemptions,
- password reset or MFA state transitions,
- single-use invitation or registration tokens,
- privilege upgrades based on temporary state,
- rate-limit counters and brute-force protections.
Where The Bug Usually Lives
The vulnerability is rarely “threads are bad” by itself. The real issue is usually one of these:
- read-modify-write sequences without locking,
- database state updated in multiple separate operations,
- business rules enforced in application code but not in the database transaction,
- external services or third-party components that are not safe under concurrency,
- temporary state transitions that are observable and reusable by parallel requests.
What To Look For
- Actions described as “one-time”, “first-come”, “single-use”, or “limited”.
- Flows that validate before they commit, especially if there is visible latency.
- Endpoints that mutate the same object repeatedly, such as cart items, wallet balances, or account settings.
- Sequences where two requests against the same account may be accepted independently.
- Features where the application returns success before all backend work is complete.
Testing Approach
- Find state-changing actions with business value.
- Map the exact validation and update sequence.
- Send two or more identical requests at the same time.
- Try mixing complementary actions, such as apply coupon plus checkout, or withdraw plus transfer.
- Watch for duplicate success responses, inconsistent balances, reused tokens, or impossible state transitions.
The goal is not just “make requests fast.” The goal is to hit the narrow window between check and update.
Why Parallel Requests Matter
Single-request testing may never reveal the bug because each request looks valid in isolation. The issue only appears when two operations overlap in time and both trust stale state.
This is why race-condition testing belongs in business-logic testing, not just low-level concurrency theory.
Client-Server Context
Most web applications use a multi-tier architecture:
- Presentation tier: browser or client UI.
- Application tier: business logic in the web app.
- Data tier: the backing database.
Race conditions often happen because the application tier checks something, then the data tier changes in a separate step instead of one protected transaction.
Typical Outcomes
- double spending or undercharged transactions,
- multi-use of single-use coupons or tokens,
- bypassed rate limits,
- duplicate resource creation,
- unauthorized state transitions,
- inventory oversell or slot overbooking.
Mitigation
- Make critical check-and-update logic atomic.
- Use proper transaction isolation and row-level locking where appropriate.
- Enforce one-time semantics in the database, not only in application code.
- Use idempotency keys for repeatable client operations.
- Reduce multi-step workflows where security decisions depend on stale intermediate state.
- Design external integrations assuming retries and concurrency will happen.
Key Takeaways
- Race conditions are timing-dependent business-logic flaws, not just threading trivia.
- TOCTOU is the core pattern to recognize.
- Anything “single-use” or “limited” is worth testing with concurrent requests.
- The dangerous gap is usually between validation and state update.
- Atomic operations and database-backed guarantees are the real fixes.