FIO39-C. fflush 関数やファイル位置付け関数を呼び出さずにストリームへの入出力を交互に行わない
C99 セクション 7.19.5.3 ではストリームの更新に関して次のような制限を設けている[ISO/IEC 9899:1999]。
更新モードでオープン (mode 実引数の2番目または3番目の文字が '+' の場合) されたファイルに結び付けられたストリームに対しては、入力および出力のいずれを行ってもよい。ただし、出力の後に入力を行う場合、この2つの操作の間に fflush 関数またはファイル位置付け関数 (fseek()、fsetpos()、もしくは rewind()) を呼び出さなければならない。ただし、入力操作がファイルの終わりに達した場合を除く。入力の後に出力を行う場合、この2つの操作の間にファイル位置付け関数を呼び出さなければならない。テキストファイルを更新モードでオープンまたは生成する場合、代わりにバイナリストリームでオープンまたは生成する処理系もある。
ストリームへの出力の直後に fflush()、fseek()、fsetpos()、rewind() の呼び出しを挟まずそのストリームから入力を受け取ると、未定義の動作となる。ストリームから入力を受け取った直後に fseek、fsetpos()、rewind の呼び出しを挟まずそのストリームへ出力する場合も同様に未定義の動作となる。ただし、入力操作がファイルの終わりに達した場合を除く。したがって、同じストリームを対象とした入力と出力の間に fseek()、fflush()、fsetpos() のいずれかの関数を呼び出す必要がある(「FIO07-C. rewind() ではなく fseek() を使用すること」を参照のこと)。
違反コード
以下のコード例では、ファイルにデータを追加し、次に同じファイルからデータを読み取っている。
char data[BUFSIZ]; char append_data[BUFSIZ]; char *file_name; FILE *file; /* file_name を初期化する */ file = fopen(file_name, "a+"); if (file == NULL) { /* エラー処理 */ } /* append_data を初期化する */ if (fwrite(append_data, BUFSIZ, 1, file) != BUFSIZ) { /* エラー処理 */ } if (fread(data, BUFSIZ, 1, file) != 0) { /* データが読み取れなかった場合のエラー処理 */ } fclose(file);
しかし、fread() 呼び出しと fwrite() 呼び出しの間で flush() などを呼び出していないため、未定義の動作となる。
適合コード
以下の解決法では、出力と入力の合間に fseek() を呼び出しており、未定義の動作が起こらないようにしている。
char data[BUFSIZ]; char append_data[BUFSIZ]; char *file_name; FILE *file; /* file_name を初期化 */ file = fopen(file_name, "a+"); if (file == NULL) { /* エラー処理 */ } /* append_data を初期化 */ if (fwrite(append_data, BUFSIZ, 1, file) != BUFSIZ) { /* エラー処理 */ } if (fseek(file, 0L, SEEK_SET) != 0) { /* エラー処理 */ } if (fread(data, BUFSIZ, 1, file) != 0) { /* データを読み取れなかった場合のエラー処理 */ } fclose(file);
リスク評価
fflush 関数やファイル位置付け関数呼び出しを挟まずにストリームへの入力と出力を交互に行うと未定義の動作となる。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FIO39-C | 低 | 高 | 中 | P6 | L2 |
自動検出
CERT C Rule Pack を適用した Fortify SCA バージョン 5.0 はこのルールの違反を検知することができる。
Compass/ROSE はこのルールの単純な違反を検知することができる。
参考情報
- [ISO/IEC 9899-1999] Section 7.9.15.3, "The fopen Function"
翻訳元
これは以下のページを翻訳したものです。