You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 34 Next »

The rename() function has the following prototype.

int rename(char const *old, char const *new);

If the file referenced by new exists prior to calling rename(), the behavior is implementation-defined. On POSIX systems, the file is removed. On Windows systems, the rename() fails, returns a nonzero value, and sets errno to EACCES.

This creates issues when trying to write portable code, or when trying to get the other behavior.

  • What to do in an application targeted at only POSIX or only Windows if the "other" behavior is desired (i.e. non-clobbering on POSIX, clobbering on Windows). For non-clobbering on POSIX you use access() and only call rename() if the file does not exist. For clobbering on Windows you call remove() before rename(). In both cases there is a TOCTOU so the part about needing a secure directory applies.
  • What to do in applications intended to be portable to any C99 implementation. This is a combination of the above two cases. I.e. if you want non-clobbering you do an existence check even though it wouldn't be needed on Windows and other non-clobbering implementations; if you want clobbering you do an explicit remove() even though it wouldn't be needed on POSIX and other clobbering implementations.

For portability, you must ensure that the file referenced by new does not exist when rename() is invoked.

Compliant Solution (POSIX)

The POSIX access() function which can check for the existence of a file explicitly [[Open Group 04]]. As with the fopen() solution, this code contains an unavoidable race condition and consequently can only be safely executed within a secure directory. In fact the existence check is not really needed on POSIX systems, because POSIX specifies the behavior of rename() when the file referenced by new exists.

char const *old = /* ... */;
char const *new = /* ... */;

if (access(new,F_OK) != 0) {
  if (rename(old, new) != 0) {
    /* Handle Error */
  }
} else {
  /* Handle Error */
}

While the likelihood of access() returning a false negative is lower than that of fopen(), on file systems where the program does not have sufficient permissions in the directory to view the file, access() may return -1 even when the file exists. In such cases, rename() will also fail since the program does not have adequate permissions inside the directory.

Compliant Solution (Windows)

On Windows, rename() is already defined to return nonzero if: [[MSDN]]

File or directory specified by newname already exists or could not be created (invalid path)

So it is not necessary to explicitly check for the existence of new before calling rename().

Risk Assessment

Calling rename() has implementation-defined behavior when the new file name refers to an existing file. Incorrect use of rename() can result in a file being unexpectedly overwritten or other unexpected behavior.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

FIO10-A

medium

probable

medium

P8

L2

Related Vulnerabilities

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

References

[[ISO/IEC 9899:1999]] Section 7.9.4.2, "The rename function"
[[MSDN]] rename()
[[Open Group 04]] access()


FIO09-A. Be careful with binary data when transferring data across systems      09. Input Output (FIO)       FIO11-A. Take care when specifying the mode parameter of fopen()

  • No labels