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

ERR07-C. よりよいエラー検査を行える関数を使用する

ERR07-C. よりよいエラー検査を行える関数を使用する

ある処理を行うときの選択肢として2つの関数がある場合は、エラー検査やエラー通知の方法が優れているほうを選ぶこと。

以下の表に、エラーのチェックやレポートの機能が一部しかない、またはまったくないC言語標準ライブラリ関数と、推奨の代替関数を示す。

関数

推奨の
代替関数

コメント

atof

strtod

エラー表示なし、エラー時の動作の定義なし

atoi

strtol

エラー表示なし、エラー時の動作の定義なし

atol

strtol

エラー表示なし、エラー時の動作の定義なし

atoll

strtoll

エラー表示なし、エラー時の動作の定義なし

rewind

fseek

エラー表示なし、エラー時の障害の通知なし

setbuf

setvbuf

エラー表示なし、エラー時の障害の通知なし

違反コード (atoi())

以下のコード例は、静的配列 buff に格納されている文字列トークンを、atoi() 関数を使用して符号付きの整数値に変換している。

int si;

if (argc > 1) {
  si = atoi(argv[1]);
}

atoi()atol() および atoll() 関数は、文字列トークンをそれぞれ intlong intlong long int 表現に変換する。エラー時の動作を除き、これらは以下の関数の呼び出しに相当する。

呼出し

同等の呼出し

atoi(nptr)

(int)strtol(nptr, (char **)NULL, 10)

atol(nptr)

strtol(nptr, (char **)NULL, 10)

atoll(nptr)

strtoll(nptr, (char **)NULL, 10)

残念ながら atoi() 系の関数は無効な値に対してエラーを報告するメカニズムを持たない。特に atoi()atol()atoll() の各関数は以下の特徴を持つ。

「MSC34-C. Do not use deprecated or obsolete functions」を参照。

適合コード (strtol())

strtol()strtoll()strtoul() および strtoull() 関数は、null終端バイト文字列をそれぞれ long intlong long intunsigned 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)

Top へ

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