Time of Check to Time of Use

Time of check to time of use (TOCTTOU) is a race condition that affects software. While you read, pay attention to the mechanics of a TOCTTOU attack as provided in the attack examples. Remember the most common platform where you might find a TOCTTOU bug. What methods can be used to prevent TOCTTOU from occurring in UNIX and in Microsoft Windows?

4. Preventing TOCTTOU

Despite conceptual simplicity, TOCTTOU race conditions are difficult to avoid and eliminate. One general technique is to use exception handling instead of checking, under the philosophy of EAFP "It is easier to ask for forgiveness than permission" rather than LBYL "look before you leap" – in this case there is no check, and failure of assumptions to hold are detected at use time, by an exception.

In the context of file system TOCTTOU race conditions,the fundamental challenge is ensuring that the file system can not be changed between two system calls. In 2004, an impossibility result was published, showing that there was no portable, deterministic technique for avoiding TOCTTOU race conditions. 

Since this impossibility result, libraries for tracking file descriptors and ensuring correctness have been proposed by researchers.

An alternative solution proposed in the research community is for UNIX systems to adopt transactions in the file system or the OS kernel. Transactions provide a concurrency control abstraction for the OS, and can be used to prevent TOCTTOU races. While no production UNIX kernel has yet adopted transactions, proof-of-concept research prototypes have been developed for Linux, including the Valor file system and the TxOS kernel. Microsoft Windows has added transactions to its NTFS file system, but Microsoft discourages their use, and has indicated that they may be removed in a future version of Windows. File locking is a common technique for preventing race conditions for a single file, but it does not extend to the file system namespace and other metadata, and cannot prevent TOCTTOU race conditions. 

For setuid binaries a possible solution is to use the seteuid() system call to change the effective user and then perform the open(). Differences in setuid() between operating systems can be problematic.