Race Condition Vulnerability
Race condition occurs when multiple threads read and write the same variable i.e. they have access to some shared data and they try to change it at the same time. In such a scenario threads are “racing” each other to access/change the data. This is a major security vulnerability.
A race condition is a situation that may occur inside a critical section. This happens when the result of multiple thread execution in a critical section differs according to the order in which the threads execute. Race conditions in critical sections can be avoided if the critical section is treated as an atomic instruction. Also, proper thread synchronization using locks or atomic variables can prevent race conditions.
This is a major security vulnerability [CWE-362], and by manipulating the timing of actions anomalous results might appear. This vulnerability arises during a TOCTOU (time-of-check, time-of-use) window.
Flow of File Access during it’s TOCTOU Window
Flow of File Access during its TOCTOU Window So, what if we lock the file during this TOCTOU window itself?
- General Misconception – A trivial cure to this vulnerability could be locking the file itself during this check-and-use window because then no other process can use the file during the time window. Seems easy, then why isn’t this practical? Why can’t we use this approach to solve the race condition problem? The answer is simply that such a vulnerability could not be prevented by just locking the file.
- Problems while locking the file – A file is locked out for other processes only if it is already in the open state. This process is called the check-and-open process and during this time it is impossible to lock a file. Any locks created can be ignored by the attacking or the malicious process. What happens is that the call to Open() does not block an attack on a locked file. When the file is available for a check-and-open process, the file is open to any access/ change. So it’s impossible to lock a file at this point in time. This makes any kind of lock virtually non-existent to the malicious processes. Internally it is using the sleep_time which doubles at every attempt. More commonly this is referred to as a spinlock or the busy form of waiting. Also, there is always a possibility of the file getting locked indefinitely i.e. danger of getting stuck in a deadlock.
- What would happen even if we were somehow able to lock the file? Let’s try to lock the file and see what could be the possible drawbacks. The most common locking mechanism that is available is atomic file locking. It is done using a lock file to create a unique file on the same filesystem. We make use of link() to make a link to the lock file for any kind of access to the file.
- If link() returns 0, the lock is successful.
The most common fix available is to store the PID of the application in the lock file, which is checked against the active PID at that time. Then again a flaw with this fix is that PID may have been reused.
- Actual Solutions – A better solution is to rather than creating locks on the file as a whole, lock the parts of the file to different processes. Example – When a process wants to write into a file, it first asks the kernel to lock that file or a part of it. As long as the process keeps the lock, no other process can ask to lock the same part of the file. Hence you could see that the issue with concurrency is getting resolved like this. In the same way, a process asks for locking before reading the content of a file, which ensures no changes will be made as long as the lock is kept. Differentiating this different kind of locks is done by the system itself. The system has the capability to distinguish between the locks required for file reading and those required for file writing. This kind of locking system is achieved by the flock() system call. Flock() call can have different values :
- LOCK_SH (lock for reading)
- LOCK_EX (for writing)
- LOCK_UN (release of the lock)
Using these separate calls we can tell what kind of locks are necessary. A point to note here is that many processes can be benefited from a reading lock simultaneously since no one will attempt to change the file content. However, only one process can benefit from a lock for writing at a given time which is currently using it. Thus no other lock can be allowed at the same time, even for reading. This kind of carefully crafted system works well with applications that can ask the kernel to reserve their access (their lock) before reading or writing to an important system file. Hence this way of selectively locking the file is much practical than our initial approach. So, while you are trying to implement your file system for directories you could take advantage of this secure coding technique to prevent a potential CWE-362 (Race Condition Vulnerability).
Unlock the Power of Placement Preparation!
Feeling lost in OS, DBMS, CN, SQL, and DSA chaos? Our Complete Interview Preparation
Course is the ultimate guide to conquer placements. Trusted by over 100,000+ geeks, this course is your roadmap to interview triumph.
Ready to dive in? Explore our Free Demo Content and join our Complete Interview Preparation