EMZETT.
Login

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 = used

Both 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 = false and 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, pytest with 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