Many common operating systems, such as Windows and UNIX, support symbolic (soft) links. Symbolic links can be created in UNIX using the ln -s command or in Windows by using directory junctions in NTFS or the Linkd.exe (Win 2K resource kit) or "junction" freeware.
If not properly performed, checking for the existence of symbolic links can lead to time of creation to time of use (TOCTOU) race conditions.
...
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 non-compliant 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 *filename/; struct stat lstat_info; int fd; /* ... */ if (lstat(filename, &lstat_info) == -1) { /* handleHandle error */ } if (!S_ISLNK(lstat_info.st_mode)) { if ((fd = open(filename, O_RDWR)); if (fd == -1) { /* handleHandle error */ } } if (write(fd, userbuf, userlen); |
| Wiki Markup |
|---|
This code contains a time of creation to time of use (TOCTOU) race condition betwen the call to {{lstat()}} and the subsequent call to {{open()}} because both functions operate on a file name \[[FIO01-A. Be careful using functions that use file names for identification]\] that can be manipulated asynchronously to the execution of the program. |
< 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 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 filenamefile 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 *filename; *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 */ } if ((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 */ } } |
...
This code eliminates the TOCTOU condition because {{fstat()}} is applied to file descriptors, not file names, so the file passed to {{fstat()}} must be identical to the file that was opened. The {{lstat()}} function does not follow symbolic links, but {{open()}} does. Comparing modes using the {{st_mode}} field is sufficient to check for a symbolic link.
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 FIO05-AC. Identify files using multiple file attributes]\].
Compliant Solution (POSIX.1-2008)
| Wiki Markup |
|---|
POSIX.1-2008 \[[Austin Group 08|AA. C References#Austin Group 08]\] adds an {{fstatat()}} function. The {{fstatat()}} is similar to the {{fstat()}} function, but provides an {{AT_SYMLINK_NOFOLLOW}} flag. The {{AT_SYMLINK_NOFOLLOW}} flag instructs {{fstatat()}} to return the status of the symbolic link instead of following it, if one is specified. |
| Code Block | ||
|---|---|---|
| ||
char *filename;
struct stat fstatat_info;
int fd;
/* ... */
if (fstatat(fd, filename, &fstatat_info, AT_SYMLINK_NOFOLLOW) == -1) {
/* handle error */
}
if (!S_ISLNK(fstatat_info.st_mode)) {
if ((fd = open(filename, O_RDWR)) == -1) {
/* handle error */
}
}
write(fd, userbuf, userlen);
|
It is important in this example that filename specifies an absolute path. If path specifies a relative path, the status is retrieved from a file relative to the directory associated with the file descriptor fd.
Risk Assessment
Time of creation to time of use (TOCTOU) race condition vulnerabilities can be exploited to gain elevated privileges.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
POS35-C | 3 (high) | 3 (likely) | 2 (medium) | P18 | L1 |
)
Risk Assessment
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
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CWE 2. |
...
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" |
...
References
| Wiki Markup |
|---|
\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 9, "UNIX 1: Privileges and Files"
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.19, "Input/output <stdio.h>"
\[[MITRE 07|AA. C References#MITRE 07]\] [CWE ID 365|http://cwe.mitre.org/data/definitions/365.html], "Race Condition in Switch"
\[[Open Group 04|AA. C References#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 05|AA. C References#Seacord 05]\] Chapter 7, "File I/O" |