Time-Of-Check Time-Of-Use (TOCTOU) is a race condition subclass in which an attacker changes a resource between the program’s check on it (does the user have access? does the resource look benign?) and the program’s use of it. The check passes on the original object; the use acts on the substituted object the attacker planted in the gap. The race window is the length of time between the check and the use, and the attacker only needs one swapped state to land inside it.
Example
The check-then-open file swap
// 1. check: does the user have permission to write to "file"?if (access("file", W_OK) != 0) exit(1);// 2. attacker races the gap: replace "file" with a symlink to /etc/passwdsymlink("/etc/passwd", "file");// 3. use: open and write to "file", now a symlink to /etc/passwdint fd = open("file", O_WRONLY);write(fd, buffer, sizeof(buffer));
the check passes on the real file, which the user owns and can write to;
the swap replaces file with a symbolic link to /etc/passwd during the window between the access and the open;
the use opens the symbolic link and writes to /etc/passwd with the program’s (typically elevated) privileges.
The program did exactly what its code says. The vulnerability is that between the name check and the name use, the name referred to two different files.
Mitigation
Bind to the descriptor, not the name
The structural fix is to open the file once and perform all subsequent checks and uses on the resulting file descriptor, not the file name. A descriptor refers to the opened file once and for all — it does not follow symlink substitutions retroactively.
int fd = open("file", O_WRONLY | O_NOFOLLOW);if (fd < 0) exit(1);struct stat st;fstat(fd, &st); // check on the descriptor, not the nameif (!user_can_write(st)) { close(fd); exit(1); }write(fd, buffer, sizeof(buffer)); // the same fd used here
The check (fstat on fd) and the use (write on fd) now act on the same object by construction, because both go through the descriptor that was opened once.
Relation
Race condition as the general pattern
TOCTOU is the security-flavoured instance of the race condition concurrency concept: there the uncontrollable timing is OS scheduling of threads; here the uncontrollable timing is the attacker racing to swap the resource between check and use. The same property — output depending on uncontrollable timing — yields, for security, a window the attacker can deliberately land in.