JPCERT コーディネーションセンター

FIO42-C. 使う必要がなくなったファイルはクローズする

fopen() もしくは freopen() 関数の呼び出しと、fclose() の呼出しは、1対1で対応していなければならない。 この対応付けは、関数呼び出しの戻り値を格納するポインタオブジェクトの生存期間が終了する前か、あるいは、プログラムが正常に終了するかのいずれか早い方よりも前に行われなければならない。

一般に、このルールは資源のオープンとクローズを行う他の関数にも適用すべきである。たとえば、POSIX の open()close() 関数、 Microsoft Windows の CreateFile()CloseHandle() 関数などは適用の対象となる。

違反コード

次のコード例はルールに違反している。func() 関数が呼び出し元に戻る前に、fopen() でオープンしたファイルがクローズされていないからである。

#include 
 
int func(const char *filename) {
  FILE *f = fopen(filename, "r");
  if (NULL == f) {
    return -1;
  }
  /* ... */
  return 0;
}
適合コード

次の適合コード例では、f が指し示すファイルが、呼び出し元に戻る前にクローズされている。


#include 
  
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 
#include 
  
int main(void) {
  FILE *f = fopen(filename, "w");
  if (NULL == f) {
    exit(EXIT_FAILURE);
  }
  /* ... */
  exit(EXIT_SUCCESS);
}
適合コード (exit())

次の適合コードでは、exit() を呼び出す前に明示的に f をクローズしている。そのため、ファイルを強制的に書き出しているときやファイルをクローズしているときにエラーが発生しても、適切に処理される。

#include 
#include 
 
int main(void) {
  FILE *f = fopen(filename, "w");
  if (NULL == f) {
    /* エラー処理 */
  }
  /* ... */
  if (fclose(f) == EOF) {
    /* エラー処理 */
  }
  exit(EXIT_SUCCESS);
}
違反コード (POSIX)

次のコードは、func() が呼出し元に戻る前に、open() を呼び出して確保した資源をクローズしていないため、ルールに違反している。

#include 
#include 
 
int func(const char *filename) {
  int fd = open(filename, O_RDONLY, S_IRUSR);
  if (-1 == fd) {
    return -1
  }
  /* ... */
  return 0;
}
適合コード (POSIX)

次の適合コードでは、呼出し元に戻る前に fd がクローズされている。

#include 
#include 
#include 
  
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 
 
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 
  
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 を使ってこのルールの違反を検出できる。

Klocwork

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
翻訳元

これは以下のページを翻訳したものです。

FIO42-C. Close files when they are no longer needed (revision 25)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter