Time-of-Check to Time-of-Use (TOCTOU) Bugs: How Race Conditions Bypass Security
Race conditions allow attackers to swap data between a security check and its use, turning valid permissions into unauthorized access without breaking the code logic.
Time-of-check to time-of-use bugs occur when a system checks a condition, then uses the result after the condition has changed. Attackers exploit this gap to bypass permissions. These race conditions affect file systems, network services, and authentication flows. Mitigation requires atomic operations and locked resources.
The Race Between Checking and Using
Imagine you check the ice on a lake to ensure it is safe to walk. You step onto the ice, but a boat passes over your spot, cracking it just before your weight lands. The check was correct, but the use happened after the state changed. Time-of-check to time-of-use (TOCTOU) bugs follow this exact pattern in software.
A TOCTOU bug is a race condition. It occurs when a program checks a property of a resource, such as file permissions or user identity, and then uses that resource after the property has changed. The window between the check and the use is the vulnerability. Attackers manipulate the resource during that window to subvert the security decision.
This flaw exists because operating systems are multi-threaded. Multiple processes run simultaneously. The scheduler switches between them at unpredictable intervals. If your security check runs on one thread and the action runs on another, or if an attacker interrupts the flow, the state can shift. The system trusts the old state, but the reality has moved on.
At a Glance: TOCTOU Mechanics
| Aspect | Detail |
|---|---|
| Core Mechanism | State changes between verification and execution |
| Primary Cause | Non-atomic operations on shared resources |
| Common Targets | File systems, temporary directories, authentication tokens |
| Attack Vector | Symlink swaps, permission changes, signal injection |
| Mitigation | Atomic operations, file locking, privilege separation |
| Detection | Code review, static analysis, fuzzing with thread sanitizers |
How the Window Opens
The vulnerability arises from a lack of atomicity. An atomic operation is one that completes entirely or not at all, with no intermediate states visible to other processes. When a check and a use are separate steps, the system is not atomic.
Consider a web server that validates a user’s permission to read a file. It checks if the user has read access. If yes, it opens the file. Between those two lines of code, an attacker with write access can replace the file with a sensitive system configuration file. The server opens the new file, having already validated access to the old one.
This happens in memory as well as on disk. A pointer might be validated, then dereferenced. If another thread frees the memory and reallocates it for different data, the pointer now points to unauthorized memory. This is a use-after-free variant of the same race condition. The logic assumes stability where none exists.
Common Forms of TOCTOU Attacks
File system races are the most prevalent form. Attackers use symbolic links to redirect file operations. Suppose a service creates a temporary file, sets restrictive permissions, and then writes data. An attacker can replace that temporary file with a symlink to a critical system file before the write occurs. The service writes its data into the system file, potentially corrupting it or injecting code.
Another form involves credential validation. A system might check if a password is correct, then grant a session token. If the check and grant are not locked together, an attacker can trigger a password change during the gap. The system grants access based on the old password validation, even though the account is now locked or changed.
Network services also suffer. A server might check the source IP address of a packet to allow access. It then processes the payload. If the attacker can spoof the IP header or manipulate the routing table between the check and the processing, they bypass the network filter. This is less common in modern stacks but remains a risk in custom protocol implementations.
Who Gets Hit
Any software that manages shared resources is at risk. Web servers handling user uploads are frequent targets. They validate file types and permissions before moving files to permanent storage. If the move is not atomic, attackers can swap the file path.
Database applications are also vulnerable. They check user privileges before executing queries. If privilege checks are cached or performed separately from query execution, race conditions can allow privilege escalation.
Embedded systems and industrial control systems are particularly dangerous. They often run legacy code with poor concurrency controls. A TOCTOU bug in a control system could allow an attacker to bypass safety checks, leading to physical damage. Medical device vulnerabilities often stem from similar legacy code issues, where real-time constraints force developers to skip proper locking mechanisms.
What People Usually Get Wrong
Many developers believe that setting strict file permissions is enough. They set a file to read-only for the owner and assume it is secure. This fails because the check for permissions and the opening of the file are separate system calls. An attacker can change the permissions or the file itself between those calls.
Another misconception is that single-threaded applications are immune. They are not. Signals can interrupt the main thread. If a signal handler modifies the state that the main thread is checking, a race condition exists. The application is single-threaded, but the operating system introduces concurrency through interrupts.
Developers also often think that checking the file type after opening it is safe. This is false if the check relies on metadata that can be swapped. The application must verify the content, not just the name or type, and it must do so within a locked context.
Reducing the Risk
The primary defense is atomicity. Use system calls that perform the check and the use in a single step. For example, open a file with flags that prevent symlink following. This ensures the file you open is the file you checked.
File locking is another critical measure. Lock the resource before checking it. Hold the lock until the operation is complete. This prevents other processes from modifying the resource during the window. Use advisory locks or mandatory locks depending on the operating system support.
Privilege separation limits the damage. Run the check in a high-privilege context and the use in a low-privilege context. If a race condition occurs, the low-privilege process cannot perform destructive actions. This aligns with best practices for broken access control.
Code reviews must focus on concurrency. Look for any gap between a security decision and its enforcement. Static analysis tools can help identify potential race conditions. Dynamic testing with fuzzing can expose timing issues. Thread sanitizers are particularly effective at finding data races.
Long-Term Mitigation Strategies
Fixing TOCTOU bugs often requires rewriting core logic. You may need to refactor code to use atomic operations. This can be difficult in legacy systems. In such cases, virtual patching can provide a temporary shield. It intercepts the malicious behavior at the network or application layer without changing the code.
Regular updates are necessary. New vulnerabilities are discovered constantly. CVE monitoring helps you track known issues. However, TOCTOU bugs are often zero-days because they depend on specific timing and environment conditions. They are not always reported.
Training developers on concurrency is vital. They must understand that the operating system is not deterministic. They must design for change. Every shared resource must be treated as mutable. Every check must be paired with an immediate, locked use.

Related Vulnerability Classes
TOCTOU bugs often overlap with other vulnerability classes. Broken access control is the broader category. TOCTOU is a specific way to bypass access control. Understanding the difference helps in selecting the right mitigation.
Firmware update management is another area where TOCTOU can occur. If the verification of the firmware signature and the flashing of the firmware are not atomic, an attacker can swap the firmware during the update. This leads to persistent compromise.
OpenSSL vulnerabilities have included race conditions in the past. While OpenSSL has improved its concurrency model, custom implementations often lag behind. Review third-party libraries for known race conditions.
CWE weakness categories provide a structured way to classify these bugs. CWE-367 is the specific identifier for TOCTOU. Using this classification helps in tracking and fixing these issues across large codebases. It ensures that security teams speak a common language.
Key takeaways
- Race conditions exploit the delay between verifying a state and acting on that state.
- Atomic operations prevent external changes during critical security checks.
- File system permissions are often insufficient to stop TOCTOU attacks.
- Virtual patching can provide immediate defense while code fixes are developed.
Race conditions exploit the gap between checking a state and using it, allowing attackers to bypass security controls. Audit your code for non-atomic operations and implement file locking or privilege separation to close this window.
Frequently asked questions
How do I detect a TOCTOU bug in my code?
Look for separate system calls that check a condition and then act on it. Use static analysis tools that flag race conditions. Perform code reviews focused on concurrency and shared resources.
Can single-threaded applications have TOCTOU bugs?
Yes. Signals and interrupts can change the state between checks and uses. Even without multiple threads, the operating system can introduce concurrency through asynchronous events.
Is setting file permissions enough to prevent TOCTOU?
No. Permissions are checked before the file is opened. An attacker can change the file or its permissions in the gap between the check and the open call. Use atomic open flags instead
How this guide was produced: written by the Patch Gazette editorial team with AI assistance, checked against the public references listed below, and reviewed when the facts change. See our editorial policy or report an error.




