Race conditions and TOCTOU: when timing becomes a vulnerability
In short: A race condition arises when the result depends on the random timing of parallel processes; with check-then-use (TOCTOU) the state can change in between, e.g. with vouchers, transfers or files.
In more detail: When two requests read and change the same data at the same time, the result can become wrong. This is a concurrency bug and at the same time a security hole if it undermines rules (voucher redeemed twice, double payout).
In Depth
Example: redeeming a voucher
Request A: reads voucher (unused) Request B: reads voucher (unused)
A: applies discount B: applies discount
A: sets voucher = used B: sets voucher = usedBoth requests saw “unused”: the voucher was redeemed twice. Likewise: spending balance twice, bypassing limits (order quantity, likes, login attempts), discount codes, stock (overselling). Attackers send many parallel requests (race window), sometimes with the HTTP/2 “single-packet” technique.
TOCTOU with files and permissions
Time of check to time of use: first check (access() allowed?), then use (open()): in between someone can replace the file with a symlink to a protected file. Likewise with temporary files with predictable names. Solution: atomic operations (open with O_EXCL|O_NOFOLLOW, mkstemp), check on the opened file descriptor, not on the path.
Defence
- Atomic database operations:
UPDATE vouchers SET used = true WHERE id = ? AND used = falseand check the number of affected rows (only 1 request wins). - Transactions with suitable isolation (
SERIALIZABLE) or row locks (SELECT … FOR UPDATE), optimistic locking (version column). - Unique constraints (UNIQUE), idempotency keys for payments/orders.
- Mutex/locks, with several servers distributed locks (Redis/DB) with caution.
- Rate limiting, queues (processing one after another).
- Do not rely on “first read, then write” in application code.
Testing and classification
- Parallel load test (e.g. 50 simultaneous redemptions) in your own test environment; tools such as
k6,wrk,pytestwith threads, Burp Turbo Intruder (own/authorised systems only). - Code review: every sequence “check → change” on shared data is suspicious.
- Database integrity (constraints) is the last line of defence.
- Classification: CWE-362/367, part of business logic flaws; also in operating systems (Dirty COW) and smart contracts.
- Related: deadlock, mutex, ACID transactions, rate limiting.
See also: Database transactions and ACID, Mutex, Deadlock, Rate limiting