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

INT08-C. すべての整数値が範囲内にあることを確認する

INT08-C. すべての整数値が範囲内にあることを確認する

整数演算の結果の値は、整数型の範囲におさまらなければならない(つまり、結果の値は範囲制限のない整数により生成された結果と等しくなること)。整数値の使い方によっては、必要な範囲がより制限される場合もよくある。例えば、整数値をループのインデックスとして使用する場合など。整数値が正当なものかどうかはコードレビューや静的解析を行うことで確認することができる。

整数オーバーフローは未定義の動作である。こうなるとコンパイルされたプログラムはなにをしてもよい。ちょっくら横道にそれて人生ゲームをプレイするなんてのもあり。その上コンパイラは、オーバーフローは絶対に起こらないと仮定して最適化を行い、その結果、予期せぬ結果をもたらす可能性もある。コンパイラは、最適化により、オーバーフローが起きるかどうかをチェックする if 文を削除してしまうかもしれない。「MSC15-C. 未定義の動作に依存しない」を参照のこと。

演算が範囲内におさまることをチェックする方が、範囲外の値をエラー条件として取り扱うよりも望ましい。なぜなら、こうしたエラー処理は実アプリでサービス運用妨害(DoS)を引き起こすことが何度も示されているからである。この失敗の最たる例として、アリアン5ロケット打ち上げの失敗がある。この失敗は、変換エラーを適切に処理しなかった結果プロセッサがシャットダウンされたことが原因で発生した[Lions 1996]。

整数オーバーフローが起こるのを目前にしたプログラムは以下の2つのうちいずれかを行うだろう。(1) ある種のエラー条件をシグナルする。あるいは、(2)システム上で表現可能な範囲の整数値を生成する。あるケースでは、エラー条件として処理し、制御フローを変更する(たとえば、システムが悪い入力に文句を言って、ユーザに別の入力を求める、など)。他のケースは、後者の方法でよりうまく処理できる。なぜなら、処理は継続し、整数値を結果として生成するから。これによりサービス運用妨害(DoS)を回避することができる。しかし、オーバーフロー時に処理を継続する場合は、どんな整数値をユーザに返すかということが問題になる。

以下のセクションでは、整数値の制限された範囲を扱う saturation および modwrap のアルゴリズムと手法を定義する。これらは、必ずあらかじめ定められた範囲の整数値を返す。この範囲は MINMAX (それ自身も含む)の間にある。MINMAX はそれぞれ MIN < MAX を満たす、表現可能な値である。

Saturation セマンティクス

演算の数学的結果を result とするとき、saturation セマンティクスでは、ユーザに実際に返される値は、以下の表のようになる:

数学的結果の範囲

返される値

MAX < result

MAX

MIN <= result <= MAX

result

result < MIN

MIN

Modwrap セマンティクス

modwrap セマンティクス (剰余(モジュロ)演算とも呼ばれる)では、整数値が"ラップアラウンド"する。つまり、MAX に1足すと MIN になる。これはC標準 [ISO/IEC 9899:2011] で定義された符号無し整数の動作であり(C99のセクション6.2.5、9段落目を参照)、また符号付き整数に関しても同様の動作になる場合が多い。しかし、多くのアプリケーションでは、modwrap セマンティクスよりも saturation セマンティクスを使用する方が理にかなっていることが多い。たとえば、サイズを(符号無し整数を使用して)計算するときなど、オーバーフローが起きたときに、値は最大値にとどまる方が、突然非常に小さい値になるよりも好ましい場合が多い。

制限された範囲での使用

整数オーバーフローを避けるもう1つの方法として、符号付き整数の半分の範囲の値のみ使用するという手もある。たとえば、int を使用するときは、[INT_MIN/2, INT_MAX/2] の範囲のみ使用するのである。これは Fortran ではかねてから知る人ぞ知るテクニックとして用いられていたが、Cコンパイラの最適化も近年洗練されてきたことから、C言語でも有効なテクニックとなっている。

減算について考えてみる。もしユーザが a - b という式を書いて ab[INT_MIN/2, INT_MAX/2] の範囲に収まるなら、その計算結果は、典型的な2の補数表現のマシンでは (INT_MIN, INT_MAX] の範囲に収まる。

次に、ユーザが a < b と書いたらどうなるだろうか。多くの場合、暗黙裡に減算が行われる。コンディションコードを持たないマシンでは、コンパイラは減算命令を発行してその結果が負でないかどうかをチェックする。これが許されるのは、コンパイラはオーバーフローが起こらないと想定して動作してもよいからである。もしユーザが生成する値がすべて明示的に [INT_MIN/2, INT_MAX/2] の範囲に保たれるなら、コンディションコードを持たないマシンで前述のような最適化が行われたとしても、比較演算は常に正しく行われる。

違反コード

以下のコード例では、i + 1 は16ビットマシンでオーバーフローする。C言語標準は、符号付き整数がオーバーフローして間違った結果を生成することを許している。コンパイラは、これを利用し、オーバーフローが発生しないと仮定することでより高速なコードを生成してもよい。結果として、オーバーフローを捕獲する意図で書かれた if 文が最適化により取り除かれるかもしれない。

int i = /* 32767 という値に評価される式が入る */;
/* ... */
if (i + 1 <= i) {
  /* オーバーフローの処理 */
}
/* i + 1 を含む式が入る */
適合コード

int のかわりに long を使用することで、計算結果を格納できることが保証される。

long i = /* 32767 という値に評価される式が入る */;
/* ... */
/* i をテストしなくても、オーバーフローしないことが分かっている */
/* i + 1 を含む式が入る */
リスク評価

範囲外の整数値を使用すると、任意のメモリ位置の読み書きを行ってしまったり、任意のコード実行を引き起こすおそれがある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

INT08-C

P4

L3

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

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

オーバーフローするおそれのある加算を含む式を警告することで、このレコメンデーションの違反を検出することができる。たとえば、a + b < c (bc がコンパイル時定数) と b > c を比較する代わりに、コードで a < c - b と比較する。(これは、a, b, c が符号なし int であることを前提としている。通常、b は小さく、cINT_MAX などの上限である)

LDRA tool suite

V. 8.5.4

488 S

部分的に実装済み

PRQA QA-C 8.1

0272 (I)
0273 (I)

部分的に実装済み
関連するガイドライン
CERT C++ Secure Coding Standard INT08-CPP. Verify that all integer values are in range
ISO/IEC TR 24772:2013 Numeric Conversion Errors [FLC]
参考資料
[Lions 1996]
翻訳元

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

INT08-C. Verify that all integer values are in range (revision 66)

Top へ

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