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. For portability, you must ensure that the file referenced by new
does not exist when rename()
is invoked.
Non-Compliant Code Example
In this non-compliant code example, a file is moved using rename()
.
char const *old = /* ... */; char const *new = /* ... */; if (rename(old, new) != 0) { /* Handle Error */ }
If the file named by new
exists at the time of the call to rename()
, the result is implementation-defined.
Compliant Solution
This compliant solution checks for the existence of the new file before callling rename()
. This code contains an unavoidable race condition between the call to fopen()
and the call to rename()
. Consequently, this code can only be safely executed within a secure directory.
char const *old = /* ... */; char const *new = /* ... */; FILE *file = fopen(new, "r"); if (!file) { if (rename(old, new) != 0) { /* Handle Error */ } } else { fclose(file); /* handle Error */ }
Unfortunately, fopen()
may fail on many file systems when the file exists but the program does not have sufficient permissions to read it.
Compliant Solution (POSIX)
A somewhat better solution involves using 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, _access_s()
allows one to check for the existence of a file [[MSDN]]. As with the fopen()
solution, this code contains an unavoidable race condition and consequently can only be safely executed within a secure directory.
char const *old = /* ... */; char const *new = /* ... */; if (_access_s(new,0) != 0) { if (rename(old, new) != 0) { /* Handle Error */ } } else { /* Handle Error */ }
This _access_s()
example shares many of the same problems as the POSIX access()
example above.
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]] _access_s, _waccess_s
[[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()