ERR07-C. よりよいエラー検査を行える関数を使用する
ある処理を行うときの選択肢として2つの関数がある場合は、エラー検査やエラー通知の方法が優れているほうを選ぶこと。
以下の表に、エラーのチェックやレポートの機能が一部しかない、またはまったくないC言語標準ライブラリ関数と、推奨の代替関数を示す。
関数 |
推奨の |
コメント |
---|---|---|
|
|
エラー表示なし、エラー時の動作の定義なし |
|
|
エラー表示なし、エラー時の動作の定義なし |
|
|
エラー表示なし、エラー時の動作の定義なし |
|
|
エラー表示なし、エラー時の動作の定義なし |
|
|
エラー表示なし、エラー時の障害の通知なし |
|
|
エラー表示なし、エラー時の障害の通知なし |
違反コード (atoi()
)
以下のコード例は、静的配列 buff
に格納されている文字列トークンを、atoi()
関数を使用して符号付きの整数値に変換している。
int si; if (argc > 1) { si = atoi(argv[1]); }
atoi()
、atol()
および atoll()
関数は、文字列トークンをそれぞれ int
、long int
、long long int
表現に変換する。エラー時の動作を除き、これらは以下の関数の呼び出しに相当する。
呼出し |
同等の呼出し |
---|---|
|
|
|
|
|
|
残念ながら atoi()
系の関数は無効な値に対してエラーを報告するメカニズムを持たない。特に atoi()
、atol()
、atoll()
の各関数は以下の特徴を持つ。
- エラー時に
errno
をセットする必要がない。 - 結果の値が表現できない場合、その動作は未定義とする(C言語仕様の付属書Jに記載されている未定義の動作119を参照)。
- 引数文字列が整数を表現していなければ0を返す(0を表す正しい形式の引数文字列の場合と区別がつかない)。しかし、C言語仕様はこれらの関数の正常終了時の動作しか規定していない。
「MSC34-C. Do not use deprecated or obsolete functions」を参照。
適合コード (strtol()
)
strtol()
、strtoll()
、strtoul()
および strtoull()
関数は、null終端バイト文字列をそれぞれ long int
、long long int
、unsigned long int
および unsigned long long int
表現にそれぞれ変換する。
この解決法では、strtol()
を使って文字列トークンを整数に変換しており、結果の値が int
の範囲にあることを保証している。
long sl; int si; char *end_ptr; if (argc > 1) { errno = 0; sl = strtol(argv[1], &end_ptr, 10); if ((sl == LONG_MIN || sl == LONG_MAX) && errno != 0) { perror("strtol error"); } else if (end_ptr == argv[1]) { if (puts("error encountered during conversion") == EOF) { /* エラー処理 */ } } else if (sl > INT_MAX) { printf("%ld too large!\n", sl); } else if (sl < INT_MIN) { printf("%ld too small!\n", sl); } else if ('\0' != *end_ptr) { if (puts("extra characters on input line\n") == EOF) { /* エラー処理 */ } } else { si = (int)sl; } }
上記の違反コード例と適合コードは、「INT06-C. 文字列トークンを整数に変換するには strtol() 系の関数を使う」で取り上げられているコードを採用した。
違反コード (rewind()
)
このコード例は、rewind()
を使用して、入力ストリームのファイル位置表示子を先頭に設定している。
char *file_name; FILE *fp; /* file_name を初期化 */ fp = fopen(file_name, "r"); if (fp == NULL) { /* オープンエラーの処理 */ } /* データの読み取り */ rewind(fp); /* 続行 */
rewind()
が成功したかどうかは判断できない。
適合コード (fseek()
)
以下の解決法は rewind()
の代わりに fseek()
を使用し、操作が成功したかどうかを確認している。
char *file_name; FILE *fp; /* file_name を初期化 */ fp = fopen(file_name, "r"); if (fp == NULL) { /* オープンエラーの処理 */ } /* データの読み取り */ if (fseek(fp, 0L, SEEK_SET) != 0) { /* 再配置エラーの処理 */ } /* 続行 */
上記の違反コード例と適合コードは、「FIO07-C. rewind() ではなく fseek() を使用する」で取り上げられているコードを採用した。
違反コード (setbuf()
)
以下のコード例は、buf
の引数に NULL
を使用して setbuf()
を呼び出す。
FILE *file; /* file の設定 */ setbuf(file, NULL); /* ... */
setbuf()
の呼び出しが成功したかどうかは判断できない。
処理系固有の詳細
4.2BSD および 4.3BSD システムの setbuf()
が使用するバッファサイズは最適ではないため、使用を避けるべきである。
適合コード (setvbuf()
)
以下の解決法は setvbuf()
を呼び出しており、この関数は操作が失敗するとゼロ以外の値を返す。
FILE *file; char *buf = NULL; /* file の設定 */ if (setvbuf(file, buf, buf ?_IOFBF : _IONBF, BUFSIZ) != 0) { /* エラー処理 * / } /* ... */
上記の違反コード例と適合コードは、「FIO12-C. setbuf() ではなく setvbuf() を使用する」で取り上げられているコードを採用した。
リスク評価
このレコメンデーションの違反がセキュリティ上の脆弱性につながることはまれだが、データの欠損や誤解釈には容易につながるおそれがある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
ERR07-C |
中 |
中 |
中 |
P8 |
L2 |
自動検出(最新の情報はこちら)
一般にこのレコメンデーションへの違反を検出することは困難であるが、よりよいエラー処理を行なう同等の関数が存在する特定の関数の使用を検出することは可能である。
関連するガイドライン
MITRE CWE | CWE-20, Insufficient input validation CWE-676, Use of potentially dangerous function |
参考資料
[Klein 2002] | "Bullet Proof Integer Input Using strtol() |
翻訳元
これは以下のページを翻訳したものです。
ERR07-C. Prefer functions that support error checking over equivalent functions that don't (revision 22)