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"
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-cpp-coding-standard/rules/input-output-fio/fio51-cpp], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

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

resources

resources : set[str] = {'FileHandle'}

Set of resources to be checked (selection of rules in the Resources group).
 

terminate_functions

terminate_functions : set[bauhaus.analysis.config.QualifiedName] = {'std::terminate', 'terminate'}

Functions that terminate program execution.
 

witness_paths

witness_paths : bool = True

Whether witness paths should be determined and included in the issue.
 

witness_should_include_exception_handling

witness_should_include_exception_handling : bool = False

Whether to only accept witness paths that include some kind of exception handling.