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

FIO37-C. fgets() や fgetws() が読み取り成功時に空でない文字列を返すと想定しない

FIO37-C. fgets() や fgetws() が読み取り成功時に空でない文字列を返すと想定しない

読み取るデータの種類について間違った想定をしていると、エラーが発生する可能性がある。たとえば、ユーザの端末からテキストデータが読み取られる、あるいはプロセスの出力が stdin にパイプされるはずが、実際にはファイルからバイナリデータが読み取られている場合には、こうした想定がくつがえされる可能性がある (「FIO14-C. ファイルストリームにおけるテキストモードとバイナリモードの違いを理解する」を参照)。システムによっては、キーボードから null バイト(や他のバイナリデータ)を入力できる場合もある。

C 言語規格 [ISO/IEC 9899:2011] 7.21.7.2 節には、次のように記載されている。

fgets 関数は、成功すると s を返す。ファイルの終わりを検出し、かつ配列に1文字も読み取っていなかった場合、配列の内容を変化させずに残し、空ポインタを返す。

ワイド文字を読み取る fgetws() 関数も同様の動作をする。したがって、fgets() あるいは fgetws()が null でないポインタを返すならば、配列には実際にデータが格納されていると仮定しても安全であろう。しかし、配列に空でない文字列が含まれていると仮定するのは間違いである。なぜなら、データにはナル文字が含まれているかもしれないからだ。

違反コード

次の違反コード例では、入力行から末尾の改行 (\n) を削除しようとしている。fgets() 関数は、通常、改行で終わる行を入力ストリームから読み込むために使用される。fgets() は、サイズ引数をとり、ストリームから文字配列へ、最大で size - 1 文字までコピーする。

#include <stdio.h>
#include <string.h>
 
enum { BUFFER_SIZE = 1024 };
 
void func(void) {
  char buf[BUFFER_SIZE];
 
  if (fgets(buf, sizeof(buf), stdin) == NULL) {
    /* エラー処理 */
  }
  buf[strlen(buf) - 1] = '\0';
}

strlen() 関数は、終端ナル文字より前にある文字数を数えることで、文字列長を計算する。fgets() が入力から読み取る最初の文字が偶然ナル文字である場合、問題が発生する。問題が発生するのは、たとえば fgets() がバイナリデータファイルを読み取る場合などである [Lai 2006]。buf の最初の文字がナル文字の場合、strlen(buf) は 0 を返すので、式 strlen(buf) - 1 はラップアラウンドして大きな正の値になり、境界外書込みエラーが発生する。

適合コード

次の適合コードでは、strchr() を使い、文字列中に改行文字が存在する場合にそれを置き換えるという操作を行っている。

#include <stdio.h>
#include <string.h>
 
enum { BUFFER_SIZE = 1024 };
 
void func(void) {
  char buf[BUFFER_SIZE];
  char *p;
 
  if (fgets(buf, sizeof(buf), stdin)) {
    p = strchr(buf, '\n');
    if (p) {
      *p = '\0';
    }
  } else {
    /* エラー処理 */
  }
}
リスク評価

文字データが読み取られていると間違って想定すると、配列境界外書き込みやその他の欠陥を作り込むことにつながる。

ルール

深刻度

可能性

修正コスト

優先度

レベル

FIO37-C

P12

L1

自動検出(最新の情報はこちら

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

このルールの違反を一部検出できる。たとえば、先に挙げた違反コード例の場合であれば、fgets() とそれに続く strlen() - 1 である。strlen() - 1 の結果は -1 となる場合がある。このルールの重要な点は、最初の文字が '\0' だった場合、fgets() が返す文字列は空文字列になるという点にある。おそらく他にも違反コード例は考えられるだろうから、ROSE が検出できるようあらかじめ列挙しておく必要がある。

Fortify SCA

5.0

 

 

関連するガイドライン
CERT C Secure Coding Standard FIO14-C. ファイルストリームにおけるテキストモードとバイナリモードの違いを理解する
FIO20-C. Avoid unintentional truncation when using fgets() or fgetws()
CERT C++ Secure Coding Standard FIO37-CPP. Do not assume character data has been read
MITRE CWE CWE-119, Failure to constrain operations within the bounds of an allocated memory buffer
CWE-241, Failure to handle wrong data type
参考資料
[ISO/IEC 9899:2011] 7.21.7.2, "The fgets Function"
7.29.3.2, "The fgetws Function"
[Lai 2006]  
[Seacord 2013] Chapter 2, "Strings"
翻訳元

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

FIO37-C. Do not assume that fgets() or fgetws() returns a nonempty string when successful (revision 73)

Top へ

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