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

FLP04-C. 浮動小数点入力が例外値でないか検査する

浮動小数点数は、infinity(無限値)と NaN(not-a-number: 非数)の 2 種類の例外値をとることがある。これらの値は例外処理、または解決できない浮動小数点演算の結果として返される。 (「FLP32-C. 数学関数における定義域エラーまたは値域エラーを防止または検出する」を参照)。また、scanf や類似の関数を通じて、ユーザから直接入力されることもある。この種の値の検出および処理をしそこねると、未定義の動作を引き起こすことがある。

とくに NaN の取り扱いは問題になりやすい。式 NaN==NaN は(NaN の取り得るすべての値に対して)偽を返す。引数が NaN であるかどうかという比較の結果はすべて偽となる。また、NaN に対して算術関数を実行した結果はすべて NaN になる。それゆえ、コード内のある場所で入りこんだ NaN は、正しく取り扱わなければ、離れた場所で問題を起こす可能性がある。

scanf などの書式付き入力関数は値 INF、INFINITY、NAN(大文字小文字を区別しない)を書式指定 %f への有効な入力として受け取るため、悪意のあるユーザはこれらの値をプログラムへ直接指定することができる。したがって入力にこのような値が含まれていることが不適切な場合、入力されたすべての浮動小数点数(特にユーザによって制御されるもの)にこれらのいずれかの値が含まれていないことを確認する必要がある。<math.h> ライブラリはこの目的のために、isinf および isnan という 2 つのマクロを提供している。

isinf および isnan

isinf マクロは浮動小数点数が無限値でないかを判定する。val が無限値(正または負)であれば isinf(val) は 0 以外の値となり、それ以外の場合は 0 となる。

isnan は入力が NaN かどうかを判定する。val が NaN であれば isnan(val) は 0 以外の値となり、それ以外の場合は 0 となる。

無限値や NaN 値をプログラムに入力させたくない場合、これらのマクロを使用して、脆弱な関数に渡されないようにする必要がある。

違反コード

以下のコードは、検証を行わずにユーザからデータを受け取っている。

float currentBalance; /* ユーザの現金残高 */
void doDeposit() {
  float val;

  scanf("%f", &val);

  if(val >= MAX_VALUE - currentBalance) {
    /* 範囲エラー処理 */
  }

  currentBalance += val;
}

val に無効な値が入力され、それ以降の計算に使用、または制御値として使用された場合に、これは問題となる。たとえば、ユーザがコマンドラインに文字列 "INF"、"INFINITY"、または "NAN" (大文字小文字を区別しない)を入力すると、scanf によって無限値や NaN の浮動小数点表現として解析される。これらの値を使用する以降の計算はすべて無効になり、プログラムがクラッシュしたり、サービス運用妨害(DoS)攻撃が行われたりする可能性がある。

たとえばここでは、val に対して "nan" と入力すると、currentBalance も "nan" と等しくなる、すなわち、この値が壊れてしまう可能性がある。この値が他の場所で計算に使用されていると、どの結果の値も NaN になり、重要なデータが壊れてしまう。

処理系固有の詳細

次のコードは、32 ビット GNU Linux 上の GCC 3.4.6 を使用して実行されたものである。このプラットフォームでは、FLT_MAX の値は 340282346638528859811704183484516925440.000000 である。

#include <stdio.h>

int main(int argc, char *argv[]) {
  float val, currentBalance=0;
  scanf("%f", &val);
  currentBalance+=val;
  printf("%f\n", currentBalance);
  return 0;
}

次の表は、各種の引数に対して返された currentBalance の値を示している。

入力 currentBalance
25 25.00000
infinity inf
inf inf
-infinity -inf
NaN nan
nan nan
1e9999 inf
-1e9999 -inf

この例が示すように、ユーザは例外値である無限値や NaN を入力したり、範囲外の浮動小数点数を入力することによって計算結果を強制的に無限値にすることができる。このような入力によって、以降の currentBalance の値が壊れてしまう。したがって、攻撃者によるデータの破壊が可能になり、場合によってはクラッシュにつながることがある。

適合コード

次のコードでは、入力された浮動小数点数を最初に検証してから使用している。値が無限値でも NaN でもないことを確認するための判定を行っている。

float currentBalance; /* ユーザの現金残高 */

void doDeposit() {
  float val;

  scanf("%f", &val);
  if (isinf(val)) {
    /* infinity エラー処理 */
  }
  if (isnan(val)) {
    /* NaN エラー処理 */
  }
  if (val >= MAX_VALUE - currentBalance) {
    /* 範囲エラー処理 */
  }

  currentBalance += val;
}
例外

NaN や無限値を、プログラムへの入力として受け付け可能または想定可能な場合、明示的な検査は不要である。ただしこの種のプログラムでは、これらの入力を数式内の適切でない場所で無条件に使用するのではなく、熟考の上、使用する必要がある。

リスク評価

不適切な浮動小数点数の入力の結果、間違った計算が行われ、予期せぬ結果を生じることがある。場合によってはプログラムがクラッシュし、サービス運用妨害(DoS)攻撃が引き起こされることがある。

レコメンデーション 深刻度 可能性 修正コスト 優先度 レベル
FLP04-C P2 L3
参考情報
翻訳元

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

FLP04-C. Check floating point inputs for exceptional values

Top へ

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