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

VOID. FIO35-C. sizeof(int) == sizeof(char) の場合、ファイル終端およびファイルエラーの検出には feof() と ferror() を使用する

VOID. FIO35-C. sizeof(int) == sizeof(char) の場合、ファイル終端およびファイルエラーの検出には feof() と ferror() を使用する

このガイドラインは廃止され、以下のガイドラインに統合されました。

char 型が16ビットかそれ以上であり int 型が char 型と同じサイズであるような処理系では、fgetc()getc()getchar() などの文字入出力関数の返す値が EOF であるかどうかの区別がつかないことがある。C 言語規格は、このような処理系において EOF 文字が通常の文字と区別できることを保証していない。そのため、このような処理系でストリームの終端やエラーフラグを確認するには、feof()ferror() 関数を使用する必要がある[Kettlewell 2002]。

この問題は大きな文字セットを使用するワードマシンで発生しうる。

UTF-16 では 0xFFFF が文字を表さないことが保証されており、EOF の値として −1 を使うことができる。16 ビット EUC (Exteded UNIX Code) では、どの文字についても上位バイトは 0xFF にはならないため、衝突が発生する余地はまったくない。同様に、すべての UTF-32 文字は符号付き32ビット整数としてみた場合正の値をとる。このように、今後新たな文字セットをデザインするときには、C言語におけるこの種の問題が起きないように注意する必要がある。

このガイドラインはワイド文字の扱いにも適用される。sizeof(wint_t) == sizeof(wchar_t) が成立するプラットフォームでは、ファイル終端を検出する目的でワイド文字を WEOF と比較してはいけない。Ubuntu Linux や Mac OS X が稼働する64ビットシステムでは、wint_twchar_t も32ビット幅の型である。

違反コード

以下のコード例では、ループ終了条件として文字データ cEOF と等しいかどうかをテストしている。

#include <stdio.h>

void func(void) {
  int c;

  do {
    c = getchar();
  } while (c != EOF);
}

EOF は負の値であり、他の任意の unsigned char と異なる値であることが保証されているが、int に変換された場合に他と異なる値であることは保証されない。intchar が同じサイズの場合、上記コードのループは思ったより早く終了してしまう可能性がある。

適合コード (可搬性のある方法)

以下の適合コードでは、feof() を使ってファイル終端に達しているかどうか、また、ferror() を使ってエラーが発生しているかどうかをテストしている。

#include <stdio.h>

void func(void) {
  int c;

  do {
    c = getchar();
  } while (!feof(stdin) && !ferror(stdin));
}
適合コード (明らかに可搬性のない方法)

この適合コードではアサートを使うことで、このコードが動作することが保証されるアーキテクチャでのみ実行されるようにしている。PRECISION() マクロの定義については「INT35-C. 整数型の精度を正しく求める」を参照。

#include <assert.h>
#include <stdio.h>

void func(void) {
  int c;

  assert(PRECISION(UCHAR_MAX) < PRECISION(INT_MAX));
  do {
    c = getchar();
  } while (c != EOF);
}
例外

FIO35-EX1: C 標準関数には、文字を返さないが EOF を返り値として返すものがある。このような関数には、fclose()fflush()fputs()fscanf()puts()scanf()sscanf()vfscanf()vscanf() がある。これらの関数の返り値は正しく EOF と比較することができる。

FIO35-EX2: そのプログラムが動作するすべてのプラットフォームで int が表現できる範囲が char よりも広いことが保証されるならば、文字を EOF と比較してもよい。これらの型のサイズが同じである処理系はまれであり、ほとんどの場合この条件があてはまる。

リスク評価

C 標準は、int 型が +32767 を表現でき、char 型が int 型よりも大きくないことのみを要求している。したがって、一般的ではないが、整数定数式 EOF が通常の文字と区別できないような状況、つまり (int)(unsigned char)65535 == -1 となる場合が考えられる。それゆえ、sizeof(int) == sizeof(char) であるようなまれな処理系では、feof()ferror() を使って EOF やエラーを検知しないと EOF 文字を正しく識別できなくなる恐れがある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

FIO35-C

P2

L3

自動検出

ツール

バージョン

チェッカー

説明

Fortify SCA

5.0

 

CERT C Rule Pack を使ってこのルールの違反を検出できる

関連するガイドライン
CERT C コーディングスタンダード FIO34-C. ファイルから読み込んだ文字と EOF や WEOF を区別する
DCL03-C. 定数式の値をテストするには静的アサートを使う
SEI CERT C++ Coding Standard VOID FIO35-CPP. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char)
参考資料
[Kettlewell 2002] Section 1.2, "<stdio.h> and Character Types"
[Summit 2005] Question 12.2
翻訳元

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

void FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char) (revision 86)

Top へ

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