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

INT10-C. % 演算子を使用する際、結果の剰余が正であると想定しない

C89 (および歴史的な K&R 実装)では、負のオペランドに対する剰余演算は処理系定義であった。これは C99 で変更され、C11 にも受け継がれている。

すべての C コンパイラが C 言語規格に厳密に準拠しているわけではないため、様々なプラットフォームの様々なコンパイラでコンパイルされて利用されるコードは、% 演算子の動作に依存してはならない。

C 標準 [ISO/IEC 9899:2011] セクション 6.5.5 には次のように記載されている。

/ 演算子の結果は、第1オペランドを第2オペランドで除した商とし、% 演算子の結果は剰余とする。両演算子とも、第2オペランドの値が0の場合、その動作は 未定義 とする。

および

整数同士の除算の場合、/ 演算子の結果は、代数的な商から小数部を切り捨てた値とする。商 a/b が表現できる場合、式 (a/b)*b + a%b は、a に等しくなければならない。

剰余の小数部を切り捨てることを、0方向への切捨て と呼ぶことが多い。

C の % 演算子の定義は以下の動作を示唆している:

 17 %  3  ->  2
 17 % -3  ->  2
-17 %  3  -> -2
-17 % -3  -> -2

結果の値は非除数(式中の最初のオペランド)と同じ符号を持つ。

違反コード

以下のコード例で insert() 関数が値を追加する位置は、剰余演算を使って計算している。つまり、バッファの終端に達したら、次に値を挿入する位置はバッファの先頭になる。しかし、size および indexint 型として宣言されており、正の値であるとは限らない。処理系および sizeindex の符号次第では、(index + 1) % size の結果は負の値になるかもしれず、そうなると配列 list の境界外への書き込みを行ってしまうかもしれない。

int insert(int index, int *list, int size, int value) {
  if (size != 0) {
    index = (index + 1) % size;
    list[index] = value;
    return index;
  }
  else {
    return -1;
  }
}

このコードは、「ERR02-C. 正常終了時の値とエラーの値は別の手段で通知する」にも違反している。

違反コード

剰余演算の絶対値をとれば、必ず正の値となる。

int insert(int index, int *list, int size, int value) {
  if (size != 0) {
    index = abs((index + 1) % size);
    list[index] = value;
    return index;
  }
  else {
    return -1;
  }
}

しかし、このコードは「INT01-C. オブジェクトのサイズを表現するすべての整数値に rsize_t もしくは size_t を使用する」に違反している。また、(index + 1) が符号付き整数オーバーフローを招く可能性もある。これは「INT32-C. 符号付き整数演算がオーバーフローを引き起こさないことを保証する」に違反する。

適合コード (符号無し型)

この場合最も適切な処理は、符号無しの型を使用して処理系定義の動作を排除することだ。また、「ERR02-C. 正常終了時の値とエラーの値は別の手段で通知する」に適合するために、関数の返り値は、処理が正常に完了した場合にゼロ以外を返し、value を格納するために使われた index の値は引数 result が指すオブジェクトに代入して呼出し元に返している。

int insert(size_t* result, size_t index, int *list, size_t size, int value) {
  if (size != 0 && size != SIZE_MAX) {
    index = (index + 1) % size;
    list[index] = value;
    *result = index;
    return 1;
  }
  else {
    return 0;
  }
}
リスク評価

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

INT10-C

P1

L3

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

特定の違反コード例を検出できる。% 演算の結果が負であるかもしれない場合を特定し、その結果の値が配列のインデックスに使用される場合を警告する。また、結果の値が正であることをチェックせずに値を使用するケースについて警告することもできるが、そうすると誤検出が多発するだろう。

Fortify SCA

5.0

 

CERT C Rule Packを使ってこのレコメンデーションの違反を検出できる

LDRA tool suite

V. 8.5.4

584 S

実装済み

PRQA QA-C 8.1 3103 実装済み
関連するガイドライン
CERT C++ Secure Coding Standard INT10-CPP. Do not assume a positive remainder when using the % operator
Java セキュアコーディングスタンダード CERT/Oracle 版 NUM02-J. 除算と剰余演算でゼロ除算エラーを起こさない
MITRE CWE CWE-682, Incorrect calculation
CWE-129, Unchecked array indexing
参考資料
[Beebe 2005]  
[ISO/IEC 9899:2011] Section 6.5.5, "Multiplicative Operators"
[Microsoft 2007] C Multiplicative Operators
[Sun 2005] Appendix E, "Implementation-Defined ISO/IEC C90 Behavior"
翻訳元

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

INT10-C. Do not assume a positive remainder when using the % operator (revision 69)

Top へ

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