...
If not properly performed, checking for the existence of symbolic links can lead to race conditions.
This rule is a specific instance of rule FIO45-C. Avoid TOCTOU race conditions while accessing files.
Noncompliant Code Example
The POSIX lstat() function collects information about a symbolic link rather than its target. This noncompliant code example uses the lstat() function to collect information about the file, checks the st_mode field to determine if the file is a symbolic link, and then opens the file if it is not a symbolic link.:
| Code Block | ||||
|---|---|---|---|---|
| ||||
char *filename = /* file name */;
char *userbuf = /* user data */;
unsigned int userlen = /* length of userbuf string */;
struct stat lstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
/* Handle error */
}
if (!S_ISLNK(lstat_info.st_mode)) {
fd = open(filename, O_RDWR);
if (fd == -1) {
/* Handle error */
}
}
if (write(fd, userbuf, userlen) < userlen) {
/* Handle error */
}
|
This code contains a time-of-check-, time-of-use (TOCTOU) race condition between the call to lstat() and the subsequent call to open() because both functions operate on a file name that can be manipulated asynchronously to the execution of the program. (See recommendation FIO01-C. Be careful using functions that use file names for identification.)
Compliant Solution (POSIX.1-2008 or newer)
This compliant solution eliminates the race condition by using O_NOFOLLOW to cause open() to fail if passed a symbolic link, avoiding the TOCTOU by not having a separate "check" and "use":
| Code Block | ||||
|---|---|---|---|---|
| ||||
char *filename = /* file name */; char *userbuf = /* user data */; unsigned int userlen = /* length of userbuf string */; int fd = open(filename, O_RDWR|O_NOFOLLOW); if (fd == -1) { /* Handle error */ } if (write(fd, userbuf, userlen) < userlen) { /* Handle error */ } |
Compliant Solution (POSIX.1-2001 or older)
This compliant solution eliminates the race condition by
- calling Calling
lstat()on the file name. - calling Calling
open()to open the file. - calling Calling
fstat()on the file descriptor returned byopen(). - comparing Comparing the file information returned by the calls to
lstat()andfstat()to ensure that the files are the same.
| Code Block | ||||
|---|---|---|---|---|
| ||||
char *filename = /* file name */;
char *userbuf = /* user data */;
unsigned int userlen = /* length of userbuf string */;
struct stat lstat_info;
struct stat fstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
/* handle error */
}
fd = open(filename, O_RDWR);
if (fd == -1) {
/* handle error */
}
if (fstat(fd, &fstat_info) == -1) {
/* handle error */
}
if (lstat_info.st_mode == fstat_info.st_mode &&
lstat_info.st_ino == fstat_info.st_ino &&
lstat_info.st_dev == fstat_info.st_dev) {
if (write(fd, userbuf, userlen) < userlen) {
/* Handle Error */
}
}
|
...
Comparing i-nodes, using the st_ino fields, and devices, using the st_dev fields, ensures that the file passed to lstat() is the same as the file passed to fstat(). (See recommendation FIO05-C. Identify files using multiple file attributes.)
...
TOCTOU race condition vulnerabilities can be exploited to gain elevated privileges.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level |
|---|---|---|
POS35-C | High |
Likely |
No |
No |
P9 |
L2 |
Automated Detection
Tool | Version | Checker | Description |
|---|
| Astrée |
| user_defined | Soundly supported | ||||||
| Axivion Bauhaus Suite |
| CertC-POS35 | |||||||
| Compass/ROSE |
Can detect some violations of this rule. In particular, it ensures that calls to | |||||||||
| Coverity |
| TOCTOU | Implemented | ||||||
| Helix QAC |
| DF4886, DF4887, DF4888 | |||||||
| Klocwork |
| SV.TOCTOU.FILE_ACCESS | |||||||
| Parasoft C/C++test |
| CERT_C-POS35-b | Avoid race conditions while checking for the existence of a symbolic link | ||||||
| Polyspace Bug Finder |
| CERT C: Rule POS35-C | Checks for file access between time of check and use (TOCTOU) (rule fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
ISO/IEC 9899:1999] Section 7.19, "Input/output <stdio.h>"
...
Key here (explains table format and definitions)
...
| Race condition enabling link following | 2017-07-07: CERT: Exact |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-764 and POS51-C/POS35-C
Independent( CWE-764, POS51-C, POS35-C)
CWE-764 is about semaphores, or objects capable of being locked multiple times. Deadlock arises from multiple locks being acquired in a cyclic order, and generally does not arise from semaphores or recursive mutexes.
Bibliography
| [Dowd 2006] | Chapter 9, "UNIX 1: Privileges and Files" |
| [ISO/IEC 9899:2024] | Section 7.23, "Input/output <stdio.h>" |
| [Open Group 2004] | lstat() fstat() open() |
| [Seacord 2013] | Chapter 8, "File I/O" |
...
MITRE CWE: CWE-365, "Race Condition in Switch"
Bibliography
| Wiki Markup |
|---|
\[[Dowd 2006|AA. Bibliography#Dowd 06]\] Chapter 9, "UNIX 1: Privileges and Files"
\[[Open Group 2004|AA. Bibliography#Open Group 04]\] [lstat()|http://www.opengroup.org/onlinepubs/000095399/functions/lstat.html], [fstat()|http://www.opengroup.org/onlinepubs/009695399/functions/fstat.html], [open()|http://www.opengroup.org/onlinepubs/009695399/functions/open.html]
\[[Seacord 2005a|AA. Bibliography#Seacord 05]\] Chapter 7, "File I/O" |