原文链接:

FIO51-CPP. Close files when they are no longer needed


std::basic_filebuf<T>::open() 函数的调用,在该调用返回的指针生命周期之前或者在程序正常终止之前——取决于哪个先发生,必须匹配 std::basic_filebuf<T>::close() 的调用。

记住, std::basic_ifstream<T>, std::basic_ofstream<T>, std::basic_fstream<T> 都维护了一个对 std::basic_filebuf<T> 对象的内部引用,当需要的时候会调用 open()close(). 正确地管理这些类型的对象(而不泄露)是确保遵循这条规则的充分条件。通常,最佳的解决方式是通过值语义而不是动态内存分配来使用这些流对象——确保遵循 MEM51-CPP. Properly deallocate dynamically allocated resources. 然而,在非自动调用析构函数的情景中,这依然存在不足。

不合规的代码示例

在这个不合规的代码示例中,构造一个 std::fstream 对象 file std::fstream 的构造函数调用了 std::basic_filebuf<T>::open(), 并且被 std::terminate() 调用的std::terminate_handler 默认值是 std::abort() —— 它不会调用析构函数。因此,被该对象(注:file )维护的隐式的 std::basic_filebuf<T> 对象没有被正确关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
#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();
}

这个不合规的例子和后续合规方案都假设最终会调用 std::terminate() —— 依照 ERR50-CPP. Do not abruptly terminate the program 中描述的 ERR50-CPP-EX1 异常。简洁起见,为描述这个问题的性质,省略了部分操作。

合规的代码示例

在这个合规方案中,在调用std::terminate() 前,先调用 std::fstream::close() 来保证文件资源被正确的关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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();
}

合规方案

在这个合规方案中,在调用std::terminate()前,通过 RAII 隐式关闭流对象来保证文件资源被正确关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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();
}

风险评估

不正确关闭文件可能允许攻击者耗尽系统资源,并增加风险——在 程序异常终止时间中,已被写入内存文件缓冲区的数据在将不会被刷新 (flushed)。

Rule Severity Likelihood Remediation Cost Priority Level
FIO51-CPP Medium Unlikely Medium P4 L3

Automated Detection

Tool Version Checker Description
Tool Version Checker Description
CodeSonar 6.1p0 ALLOC.LEAK Leak
Helix QAC 2021.2 C++4786, C++4787, C++4788
Klocwork 2021.1 RH.LEAK
Parasoft C/C++test 2021.1 **CERT_CPP-FIO51-a ** Ensure resources are freed
Parasoft Insure++ Runtime detection
Polyspace Bug Finder R2021b CERT C++: FIO51-CPP Checks for resource leak (rule partially covered)

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

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