FIO44-C. fsetpos() には fgetpos() が返す値を使用する
C99 のセクション 7.19.9.3 は fsetpos() の動作を次のように定めている[ISO/IEC 9899:1999]。
fsetpos 関数は、pos が指すオブジェクトの値に従って、stream が指すストリームの mbstate_t オブジェクト(もしあれば)およびファイル位置表示子を設定する。pos が指すオブジェクトの値は、同じファイルに結び付いたストリームに対するそれ以前の成功した fgetpos 関数の呼び出しから得られた値でなければならない。
したがって、fgetpos() が返す値以外のいかなる値を pos に使用しても未定義の動作となるため、これは避けるべきである。
違反コード
以下のコードでは、ファイルから3つの値を読み取り、読み取り位置をファイルの先頭に設定し、呼び出し元に戻る。
enum { NO_FILE_POS_VALUES = 3 }; int opener(FILE* file, /* ... */ ) { int rc; fpos_t offset; /* ... */ memset(&offset, 0, sizeof(offset)); if (file == NULL) { return EINVAL; } /* ファイルからのデータ読込み */ rc = fsetpos(file, &offset); if (rc != 0 ) { return rc; } /* ... */ return 0; }
しかし、fsetpos() への有効な引数は fgetpos() の返り値だけであるため、他の手段で作成された fpos_t を使用してはいけない。位置指定子はファイルの任意の位置に設定される可能性がある。
適合コード
以下の解決法では、fgetpos() を呼び出してファイル位置表示子の初期位置を格納し、後で fsetpos() を呼び出す際に読み込み位置を元に戻すために使用している。
enum { NO_FILE_POS_VALUES = 3 }; int opener(FILE* file, /* ... */) { int rc; fpos_t offset; /* ... */ if (file == NULL) { return EINVAL; } rc = fgetpos(file, &offset); if (rc != 0 ) { return rc; } /* ファイルからのデータ読み込み */ rc = fsetpos(file, &offset); if (rc != 0 ) { return rc; } /* ... */ return 0; }
リスク評価
fsetpos() を誤用すると、ファイルストリームの読み取りが意図しない位置で行われる可能性がある。この位置を指定する引数にユーザからの入力が使われている場合、ファイルから読み取られる変数をユーザが自由に操作できる可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FIO44-C | 中 | 低 | 中 | P4 | L3 |
自動検出
CERT C Rule Pack を適用した Fortify SCA バージョン 5.0 はこのルールの違反を検知することができる。
Compass/ROSE はこのルールの一般的な違反を検知することができる。しかし、fgetpos() の返り値が複数の変数にわたってコピーされてから fsetpos() に渡されるようなケースは正しく扱えない。
参考情報
- [ISO/IEC 9899-1999] Section 7.19.9.3, "The fsetpos Function"
翻訳元
これは以下のページを翻訳したものです。
FIO44-C. Only use values for fsetpos() that are returned from fgetpos()