FIO04-C. 入出力エラーを検出し、処理する
C99 [ISO/IEC 9899-1999] のセクション 7.19 に定義されている入出力関数は、失敗か成功かを返り値で明確に示す。入出力関数の終了状態を検査し、エラーは適切に処理する必要がある。
以下の表は、Richard Kettlewell による表に基き [Kettlewell 02] 入出力関数の返り値を示している。
関数 | 成功時の返り値 | エラー時の返り値 |
---|---|---|
fclose() | ゼロ | EOF (負数) |
fflush() | ゼロ | EOF (負数) |
fgetc() | 読み取った文字 | ferror() と feof() を使用 |
fgetpos() | ゼロ | ゼロ以外 |
fgets() | 文字列へのポインタ | NULL |
fprintf() | 文字数 (負でない数) | 負数 |
fputc() | 書き出した文字 | ferror() を使用 |
fputs() | 負でない数 | EOF (負数) |
fread() | 読み取った要素 | 読み取った要素 |
freopen() | ストリームへのポインタ | null ポインタ |
fscanf() | 変換の数 (負でない数) | EOF |
fseek() | ゼロ | ゼロ以外 |
fsetpos() | ゼロ | ゼロ以外 |
ftell() | ファイル位置 | -1L |
fwrite() | 書き出した要素 | 書き出した要素 |
getc() | 読み取った文字 | ferror() と feof() を使用 |
getchar() | 読み取った文字 | ferror() と feof() を使用 |
printf() | 文字数 (負でない数) | 負数 |
putc() | 書き出した文字 | ferror() を使用 |
putchar() | 書き出した文字 | ferror() を使用 |
puts() | 負でない数 | EOF (負数) |
remove() | ゼロ | ゼロ以外 |
rename() | ゼロ | ゼロ以外 |
setvbuf() | ゼロ | ゼロ以外 |
scanf() | 変換の数 (負でない数) | EOF |
snprintf() | 書き出される文字数 (負でない値) | 負数 |
sscanf() | 変換の数 (負でない数) | EOF |
tmpfile() | ストリームへのポインタ | null ポインタ |
tmpnam() | null 以外のポインタ | null ポインタ |
ungetc() | 押し戻された文字 | EOF (下記を参照) |
vfprintf() | 文字数 (負でない数) | 負数 |
vfscanf() | 変換の数 (負でない値) | EOF |
vprintf() | 文字数 (負でない値) | 負数 |
vscanf() | 変換の数 (負でない値) | EOF |
通常は、fgetc()、fputc()、getc()、getchar()、putc()、putchar()、ungetc() から EOF が返されたかを検査すれば十分である。feof() と ferror() を使って検査する必要があるケースについては、「FIO35-C. sizeof(int) == sizeof(char) の場合、EOF およびファイルエラーの検出には feof() と ferror() を使用する」を参照のこと。
ungetc() 関数は失敗してもエラー表示子を設定しないため、引数が EOF と等しくないことがわかっていない限り、確実にエラーを検査することはできない。C99 は「1 文字の押戻しを保証する」と定めているため、再度読み取る前に最大 1 文字しか押し戻さないのであれば問題はない(「FIO13-C. 読み取った文字以外は押し戻さない」を参照)。
違反コード
以下のコード例では、fseek() 関数を使用して、file が参照しているファイル内の offset の位置にファイル位置を設定する。ただし、seek 操作で入出力エラーが発生すると要求は満たされない。
FILE *file; long offset; /* file と offset を初期化 */ fseek(file, offset, SEEK_SET); /* ファイルの処理 ... */
適合コード
C99 によると、fseek() 関数は、エラーが発生したときゼロ以外の値を返す[ISO/IEC 9899:1999]。ファイルを処理する前にこの状態を検査することで、fseek() が失敗した場合にそのファイルを操作する可能性を排除する。エラーが発生していないことを確認するために返り値を必ず検査すること。エラーが発生した場合は、適切に処理すること。
FILE *file; long offset; /* file と offset を初期化 */ if (fseek(file, offset, SEEK_SET) != 0) { /* エラー処理 */ } /* ファイルの処理 ... */
リスク評価
ファイル操作エラーの検査を行わないと、予期せぬ動作を引き起こす可能性がある。
レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FIO04-C | 中 | 中 | 高 | P4 | L3 |
自動検出
Coverity Prevent の CHECKED_RETURN は、関数呼び出しから返される値の処理方法の不一致を検出する。Coverity Prevent では、このレコメンデーションに対する違反をすべて検出できるとは限らないため、さらなる検証が必要である。
Compass/ROSE は、「EXP12-C. 関数の返り値を無視しない」に対する違反がないか検査することによって、このレコメンデーションの違反を検出できる。
参考情報
- [ISO/IEC 9899:1999] Section 7.19.3, "Files," Section 7.19.4, "Operations on Files," and "File Positioning Functions"
- [Kettlewell 02] Section 6, "I/O Error Checking"
- [MITRE 07] CWE ID 391, "Unchecked Error Condition"
- [Seacord 05a] Chapter 7, "File I/O"
翻訳元
これは以下のページを翻訳したものです。