FIO42-C. 使う必要がなくなったファイルはクローズする
fopen() もしくは freopen() 関数の呼び出しと、fclose() の呼出しは、1対1で対応していなければならない。
この対応付けは、関数呼び出しの戻り値を格納するポインタオブジェクトの生存期間が終了する前か、あるいは、プログラムが正常に終了するかのいずれか早い方よりも前に行われなければならない。
一般に、このルールは資源のオープンとクローズを行う他の関数にも適用すべきである。たとえば、POSIX の open() と close() 関数、 Microsoft Windows の CreateFile() と CloseHandle() 関数などは適用の対象となる。
違反コード
次のコード例はルールに違反している。func() 関数が呼び出し元に戻る前に、fopen() でオープンしたファイルがクローズされていないからである。
#include <stdio.h>
int func(const char *filename) {
FILE *f = fopen(filename, "r");
if (NULL == f) {
return -1;
}
/* ... */
return 0;
}
適合コード
次の適合コード例では、f が指し示すファイルが、呼び出し元に戻る前にクローズされている。
#include <stdio.h>
int func(const char *filename) {
FILE *f = fopen(filename, "r");
if (NULL == f) {
return -1;
}
/* ... */
if (fclose(f) == EOF) {
return -1;
}
return 0;
}
違反コード例 (exit())
次のコード例はルールに違反している。fopen() を呼び出して確保された資源が、プログラムが終了する前にクローズされていないからである。exit() はファイルをクローズするが、ファイルの内容を強制的に書き出している (flush) ときやファイルをクローズしている時にエラーが発生すると、プログラムはエラーを知ることができない。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *f = fopen(filename, "w");
if (NULL == f) {
exit(EXIT_FAILURE);
}
/* ... */
exit(EXIT_SUCCESS);
}
適合コード (exit())
次の適合コードでは、exit() を呼び出す前に明示的に f をクローズしている。そのため、ファイルを強制的に書き出しているときやファイルをクローズしているときにエラーが発生しても、適切に処理される。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *f = fopen(filename, "w");
if (NULL == f) {
/* エラー処理 */
}
/* ... */
if (fclose(f) == EOF) {
/* エラー処理 */
}
exit(EXIT_SUCCESS);
}
違反コード (POSIX)
次のコードは、func() が呼出し元に戻る前に、open() を呼び出して確保した資源をクローズしていないため、ルールに違反している。
#include <stdio.h>
#include <fcntl.h>
int func(const char *filename) {
int fd = open(filename, O_RDONLY, S_IRUSR);
if (-1 == fd) {
return -1
}
/* ... */
return 0;
}
適合コード (POSIX)
次の適合コードでは、呼出し元に戻る前に fd がクローズされている。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int func(const char *filename) {
int fd = open(filename, O_RDONLY, S_IRUSR);
if (-1 == fd) {
return -1
}
/* ... */
if (-1 == close(fd)) {
return -1;
}
return 0;
}
違反コード (Windows)
次の違反コードでは、Microsoft Windows の CreateFile() 関数を使ってオープンされたファイルが、func() が呼出し元に戻る前にクローズされていない。
#include <Windows.h>
int func(LPCTSTR filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return -1;
}
/* ... */
return 0;
}
適合コード (Windows)
次の適合コードでは、呼出し元に戻る前に CloseHandle() を呼び出して hFile をクローズしている。
#include <Windows.h>
int func(LPCTSTR filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return -1;
}
/* ... */
if (!CloseHandle(hFile)) {
return -1;
}
return 0;
}
リスク評価
ファイルを適切にクローズしないと、システムの資源が使い果たされたり、メモリ上のファイルバッファに書き込まれたデータがプログラムの異常終了時にファイルに強制的に書き出されないリスクが高くなる。
|
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
|---|---|---|---|---|---|
|
FIO42-C |
中 |
低 |
中 |
P4 |
L3 |
自動検出(最新の情報はこちら)
このルールは ISO/IEC TS 17961:2013 で規定されているルール [fileclose] よりも制約が厳しい。したがって、アナライザは、この技術仕様書に適合していても、本コーディングルールの違反をすべては検知できない可能性がある。
|
ツール |
バージョン |
チェッカー |
説明 |
|---|---|---|---|
|
Compass/ROSE |
|||
|
Fortify SCA |
5.0 |
CERT C Rule Pack を使ってこのルールの違反を検出できる。 |
|
| V. 9.1 |
RH.LEAK |
||
|
LDRA tool suite |
V. 8.5.4 |
49 D |
実装済み |
関連する脆弱性
このルールの違反が原因で作り込まれた脆弱性は、CERT Web サイトで検索することができる。
関連するガイドライン
| CERT C++ Secure Coding Standard | FIO42-CPP. Ensure files are properly closed when they are no longer needed |
| CERT Oracle Secure Coding Standard for Java | FIO04-J. 不要になったらリソースを解放する |
| ISO/IEC TS 17961:2013 | Failing to close files or free dynamic memory when they are no longer needed [fileclose] |
| MITRE CWE | CWE-404, Improper Resource Shutdown or Release |
参考資料
| [IEEE Std 1003.1:2013[ | XSH, System Interfaces, open |
翻訳元
これは以下のページを翻訳したものです。
