Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: excess fclose() call

TOCTOU (time-of-check, time-of-use) race condition occurs is possible when a program performs two or more accesses on a filename or path. Typically concurrent processes are operating on a shared file system [Seacord 2013b]. Typically, the first access is a check to verify some attribute of the file, followed by a call to use the file. An attacker can alter the file between the two accesses, causing the check to succeed but the use to fail. Worse, the use can operate on a different file than the checkor replace the file with a symbolic or hard link to a different file. These TOCTOU conditions can be exploited when a program performs two or more file operations on the same file name or path name.

A program that performs a two or more file operation operations on a filename single file name or path twice name creates a race window between the two file operations. This race window comes from the assumption that the filename file name or path name refers to the same file same resource both times. If an attacker can modify the file, remove it, or replace it with a different file, then this assumption will not hold, and the program will behave badly.

Noncompliant Code Example

...

If an existing file is opened for writing with the w mode argument, the file's previous contents (if any) are destroyed. This noncompliant code example tries to ensure that an open will succeed by first calling the POSIX access() function, which returns zero if the file exists and can be readprevent an existing file from being overwritten by first opening it for reading before opening it for writing. An attacker can exploit the race window between the access and the open to cause fopen() to fail in spite of the check.Since this code also does not check if fopen() succeeds, it violates @ERR33-Ctwo calls to fopen() to overwrite an existing file.

Code Block
bgColor#ffcccc
langc
#include <stdio.h>
#include <unistd.h> 

int
void open_some_file(const char *file) {
  FILE *fd;f 
  if (access("./some_file", R_OK | W_OK) != 0) {
    printf("access granted.\n= fopen(file, "r");
  if (NULL fd != fopen("./some_file", "rb+");f) {
    /* File exists, readhandle fileerror */
    fclose(fd);
  }

  return 0;
}

Compliant Solution (read)

This compliant solution dispenses with an access call and merely relies on open() to verify the file.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <unistd.h> 

int open_some_file() else {
  FILE *fd; 
  fd f = fopen("./some_file", "rb+w");
    if (fdNULL !== NULLf) {
    printf("access granted.\n");
    /* readHandle fileerror */
    fclose(fd);}
  }

  return 0;
}

Noncompliant Code Example (POSIX write)

If an existing file is opened for writing, the file's previous contents are destroyed. This noncompliant code example tries to prevent an existing file from being overwritten by first ensuring that a file does not exist before opening it for writing. An attacker can exploit the race window between the access and the open to cause fopen() to overwrite an existing file.

Code Block
bgColor#ffcccc
langc
#include <stdio.h>
#include <unistd.h> 

int open_some_file() {
  FILE *fd; 
  if (access("./some_file", R_OK | W_OK) != 0) {
    printf("access granted.\n"); /* Write to file */
    if (fclose(f) == EOF) {
    fd = fopen("./some_file", "wb+");
    /* writeHandle to fileerror */
    fclose(fd);}
  }

  return 0;
}

Compliant Solution

...

This compliant solution uses the x mode of invokes fopen() at a single location and uses the x mode of fopen(), which was added to added in C11. This mode causes causes fopen() to  to fail if the file exists. This check and subsequent open is done performed without creating a race window. Note that the  The x mode  mode provides exclusive access to the file if the operating platform provides only if the host environment provides this support.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <unistd.h> 

intvoid open_some_file(const char *file) {
  FILE *fd; 
  fdf = fopen("./some_file", "wb+xwx");
  if (fdNULL == NULLf) {
     printf("access granted.\n");/* Handle error */
  }
  /* writeWrite to the file */
  if  (fclose(fd);
  }

  return 0;
}
f) == EOF) {
    /* Handle error */
  }
}

Compliant Solution (POSIX

...

)

This compliant solution uses the the O_CREAT and  and O_EXCL flags  flags of POSIX's open() system call function. These flags cause cause open() to  to fail if the file exists.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

intvoid open_some_file(const char *file) {
  int filefd = open("./some_file", O_CREAT | O_EXCL | O_RDWRWRONLY);
  if (file-1 != -1fd) {
    FILE *fd; 
    fdf = fdopen(filefd, "rw+w");
    if (fdNULL != NULLf) {
      /* Write to file */

      if printf("access granted.\n");
(fclose(f) == EOF) {
        /* writeHandle to fileerror */
      fclose(fd);}
    } else {
      if (close(file);
  }

  return 0;
}
fd) == -1) {
        /* Handle error */
      }
    }
  }
}

Exceptions

FIO45-C-EX1EX2: Accessing a path multiple times is permitted if it is requested specifically by the users. A program that accepts commands from a user to read or write a specific filename does not violate this standard. Example programs would include file managers or text editors.FIO45-EX2: Accessing a path file name or path name multiple times is permitted if the path can not be modified by an attacker. This could occur, for example, if the path refers to file referenced resides in a secure directory. (for For more information, see FIO15-C. Ensure that file operations are performed in a secure directory.).

FIO45-C-EX3: Accessing a file name or path name multiple times is permitted if the program is able to can verify that every operation indeed operates on the same file.

This POSIX code example demonstrates how to verify that two accesses are indeed verifies that each subsequent file access operates on the same file in POSIX. In POSIX, every file can be uniquely identified by using its device and i-node attributes. This code example checks that a filename does not refer to a symbolic link, using lstat(); the call also retrives a file name refers to a regular file (and not a directory, symbolic link, or other special file) by invoking lstat(). This call also retrieves its device and i-node. The file is subsequently opened. Finally, the program verifies that the file trhat that was opened is the same one (matching device and inodesi-nodes) as the file that was verified not to be a symbolic linkconfirmed as a regular file.

An attacker can still exploit this code if they have the ability to delete the benign file and create the malicious file within the race window between lstat() and open(). It is possible that the OS kernel will reuse the same device and i-node for both files. This can be mitigated by making sure that the attacker lacks the permissions to delete the benign file.

Code Block
bgColor#ccccff
langc
#include <sys/stat.h>
#include <fcntl.h>

int open_realregular_file(char *filename, int flags) {
  struct stat lstat_info;
  struct stat fstat_info;
  int fdf;
 
  if (lstat(filename, &lstat_info) == -1) {
    /* fileFile does not exist, handle error */
  }
 
  if (!S_ISLNKISREG(lstat_info.st_mode)) {
    /* fileFile is not a symlinkregular file, handle error */
  }
 
  if fd((f = open(filename, flags);
  if (fd)) == -1) {
    /* fileFile has disappeared, handle error */
  }
 
  if (fstat(fdf, &fstat_info) == -1) {
    /* handleHandle error */
  }
 
  if (!(lstat_info.st_ino !== fstat_info.st_ino  &&||
       (lstat_info.st_dev =!= fstat_info.st_dev) {
    /* openOpen file is not non-symlink the expected regular file, handle error */
  }
 
  /* fdf is truethe openexpected file,regular andopen file was not symlink */
  return fdf;
}

Risk Assessment

TOCTOU race  race conditions can result in unexpected behavior, including privilege escalation.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO32

FIO45-C

High

Probable

High

P6

L2

Automated Detection

Tool

Version

Checker

Description

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V
IO.RACEFile system race condition
Coverity
Include Page
Coverity_V
Coverity_V

TOCTOU

Implemented
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF4851, DF4852, DF4853


Klocwork
Include Page
Klocwork_V
Klocwork_V
SV.TOCTOU.FILE_ACCESS
LDRA tool suite
Include Page
LDRA_V
LDRA_V
75 DPartially implemented
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-FIO45-a

Avoid race conditions while accessing files

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule FIO45-C

Checks for file access between time of check and use (rule partially covered)

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Bibliography

[Seacord
2013
2013b]Chapter 7, "Files"


...

Image Added Image Removed Image Removed Image RemovedImage Added Image Added