CertC++-FIO51¶
Close files when they are no longer needed
Required inputs: IR, StaticSemanticAnalysis
A call to the
std::basic_filebuf<T>::open() function must be matched with
a call to
std::basic_filebuf<T>::close() before the lifetime of the
last pointer that stores the return value of the call has ended or before
normal program termination, whichever occurs first.
Note that
std::basic_ifstream<T>,
std::basic_ofstream<T>, and
std::basic_fstream<T> all maintain an internal reference to
a
std::basic_filebuf<T> object on which
open() and
close() are called as needed. Properly managing an object of one
of these types (by not leaking the object) is sufficient to ensure compliance
with this rule. Often, the best solution is to use the stream object by value
semantics instead of via dynamic memory allocation, ensuring compliance
with
MEM51-CPP.
Properly deallocate dynamically allocated resources. However, that is still
insufficient for situations in which destructors are not automatically called.
Noncompliant Code Example
In this noncompliant code example, a
std::fstream object
file is constructed. The constructor for
std::fstream calls
std::basic_filebuf<T>::open(), and the default
std::terminate_handler called by
std::terminate() is
std::abort(), which does not call destructors. Consequently, the
underlying
std::basic_filebuf<T> object maintained by the object is not
properly closed.
#include <exception>
#include <fstream>
#include <string>
void f(const std::string &fileName) {
std::fstream file(fileName);
if (!file.is_open()) {
// Handle error
return;
}
// ...
std::terminate();
}
This noncompliant code example and the subsequent compliant solutions are
assumed to eventually call
std::terminate() in accordance with the ERR50-CPP-EX1 exception
described in
ERR50-CPP.
Do not abruptly terminate the program. Indicating the nature of the problem
to the operator is elided for brevity.
Compliant Solution
In this compliant solution,
std::fstream::close() is called before
std::terminate() is called, ensuring that the file resources are
properly closed.
#include <exception>
#include <fstream>
#include <string>
void f(const std::string &fileName) {
std::fstream file(fileName);
if (!file.is_open()) {
// Handle error
return;
}
// ...
file.close();
if (file.fail()) {
// Handle error
}
std::terminate();
}
Compliant Solution
In this compliant solution, the stream is implicitly closed through
RAII before
std::terminate() is called, ensuring that the file resources
are properly closed.
#include <exception>
#include <fstream>
#include <string>
void f(const std::string &fileName) {
{
std::fstream file(fileName);
if (!file.is_open()) {
// Handle error
return;
}
} // file is closed properly here when it is destroyed
std::terminate();
}
Risk Assessment
Failing to properly close files may allow an attacker to exhaust system resources and can increase the risk that data written into in-memory file buffers will not be flushed in the event of abnormal program termination.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| FIO51-CPP | Medium | Unlikely | Medium | P4 | L3 |
Related Guidelines
This rule supplements FIO42-C. Close files when they are no longer needed.
| SEI CERT C++ Coding Standard | MEM51-CPP. Properly deallocate dynamically allocated resources |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 27.9.1, "File Streams" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
abrupt_terminate |
{} may leave file unclosed. |
None |
False |
memory_leak |
Close files when they are no longer needed. |
None |
False |
possible_memory_leak |
Close files when they are no longer needed. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
resources¶
resources : set[str] = {'FileHandle'}
terminate_functions¶
terminate_functions : set[bauhaus.analysis.config.QualifiedName] = {'std::terminate', 'terminate'}
witness_paths¶
witness_paths : bool = True
witness_should_include_exception_handling¶
witness_should_include_exception_handling : bool = False