Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This noncompliant code example creates a file with a hard-coded file_name (presumably in a shared directory such as /tmp or C:\Temp).

Code Block
bgColor#FFCCCC
langc
char file_name[] = /* hard coded string */;

FILE *fp = fopen(file_name, "wb+");
if (fp == NULL) {
  /* Handle error */
}

...

Wiki Markup
The following noncompliant code example attempts to remedy the problem by generating the file name at runtime using {{tmpnam()}}.  The C99 {{tmpnam()}} function generates a string that is a valid file name, and that is not the same as the name of an existing file \[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\]. Files created using strings generated by the {{tmpnam()}} function are temporary in that their names should not collide with those generated by conventional naming rules for the [implementation|BB. Definitions#implementation]. The function is potentially capable of generating {{TMP_MAX}} different strings, but any or all of them may already be in use by existing files.

Code Block
bgColor#FFCCCC
langc
char file_name[L_tmpnam];
FILE* fp;

if (!tmpnam(file_name)) {
  /* Handle error */
}

/* A TOCTOU race condition exists here */

fp = fopen(file_name, "wb+");
if (fp == NULL) {
   /* Handle error */
}

...

Wiki Markup
This next noncompliant code example attempts to remedy the problem by using the POSIX {{open()}} function and providing a mechanism to indicate whether an existing file has been opened for writing or a new file has been created \[[Open Group 2004|AA. Bibliography#Open Group 04]\]. If the {{O_CREAT}} and {{O_EXCL}} flags are used together, the {{open()}} function fails when the file specified by {{file_name}} already exists. To prevent an existing file from being opened and truncated, include the flags {{O_CREAT}} and {{O_EXCL}} when calling {{open()}}.

Code Block
bgColor#FFCCCC
langc
char file_name[L_tmpnam];
int fd;

if (!(tmpnam(file_name))) {
  /* Handle error */
}

/* A TOCTOU race condition exists here */

fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
   /* Handle error */
}

...

Wiki Markup
The TR 24731-1 {{tmpnam_s()}} function generates a string that is a valid file name and that is not the same as the name of an existing file \[[ISO/IEC TR 24731-1:2007|AA. Bibliography#SO/IEC TR 24731-1-2007]\]. It is almost identical to the {{tmpnam()}} function, except for an added {{maxsize}} argument for the supplied buffer.

Code Block
bgColor#FFCCCC
langc
char file_name[L_tmpnam_s];
int fd;

if (tmpnam_s(file_name, L_tmpnam_s) != 0) {
  /* Handle error */
}

/* A TOCTOU race condition exists here */
fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
  /* Handle error */
}

...

The POSIX function mktemp() takes a given file name template and overwrites a portion of it to create a file name. The template may be any file name with some number of X's appended to it (for example, /tmp/temp.XXXXXX). The trailing X's are replaced with the current process number and/or a unique letter combination. The number of unique file names mktemp() can return depends on the number of X's provided.

Code Block
bgColor#FFCCCC
langc
char file_name[] = "tmp-XXXXXX";
int fd;

if (!mktemp(file_name)) {
  /* Handle error */
}

/* A TOCTOU race condition exists here */

fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
if (fd < 0) {
  /* Handle error */
}

...

Most historic implementations provide only a limited number of possible temporary file names (usually 26) before file names are recycled.

Code Block
bgColor#FFCCCC
langc
FILE* fp = tmpfile();
if (fp == NULL) {
  /* Handle error */
}

...

After a program obtains a file name using the tmpnam_s function and before the program creates a file with that name, the possibility exists that someone else may create a file with that same name. To avoid this race condition, the tmpfile_s function should be used instead of tmpnam_s when possible. One situation that requires the use of the tmpnam_s function is when the program needs to create a temporary directory rather than a temporary file.

Code Block
bgColor#FFCCCC
langc
if (tmpfile_s(&fp)) {
  /* Handle error */
}

...

A call to mkstemp() replaces the six X's in the template string with six randomly selected characters and returns a file descriptor for the file (opened for reading and writing), as in this compliant solution.

Code Block
bgColor#ccccff
langc
const char *sdn = "/home/usr1/";
char sfn[] = "/home/usr1/temp-XXXXXX";
FILE *sfp;

if (!secure_dir(sdn)) {
  /* Handle error */
}

int fd = mkstemp(sfn);
if (fd == -1) {
  /* Handle error */
}

/*
 * Unlink immediately to hide the file name.
 * The race condition here is inconsequential if the file
 * is created with exclusive permissions (glibc >= 2.0.7)
 */

if (unlink(sfn) == -1) {
  /* Handle error */
}

sfp = fdopen(fd, "w+");
if (sfp == NULL) {
  close(fd);
  /* Handle error */
}

/* Use temporary file */

fclose(sfp); /* also closes fd */

...