SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaセキュアコーディング入門

整数オーバーフロー検出の3つのアプローチ
――mezzofantiのバグ修正

Javaセキュアコーディング入門(3)

  • X ポスト
  • このエントリーをはてなブックマークに追加

Javaにおける整数オーバーフロー

 Java言語仕様§4.2.2「整数演算」には次のように書かれています。

いかなる場合であっても、組み込みの整数演算子がオーバーフローやアンダーフローを起こすことはない。整数演算は、null参照のアンボクシング変換が要求された場合、NullPointerExceptionをスローすることができる。それ以外の場合、例外をスローできる演算子は、右手側オペランドがゼロである場合にArithmeticExceptionをスローする整数除算演算子/と整数剰余演算子%、および、ボクシング変換を必要とするものの、変換を実行するための十分なメモリが利用可能でない場合にOutOfMemoryErrorをスローするインクリメントおよびデクリメント演算子++と--のみである。

 また、§15.17.1「乗算演算子*」には次のように書かれています。

整数演算がオーバーフローした場合、結果は十分な大きさの2の補数表現によって表現された数学的な積の下位ビットとなる。

 つまり、プログラマ自身が整数オーバーフローの発生を検知するようなコードを書くか、あるいはそもそも整数オーバーフローを引き起こさないようなコードを書かない限り、オーバーフローが発生してもプログラムは黙って実行を続けるということです。オーバーフローの発生は、計算間違いやプログラムの想定外の動作を引き起こす可能性があるため、避けなければいけません。整数オーバーフローが発生する可能性があるところでは、明示的に整数オーバーフローをチェックすべきです。ではどのように書けばよいか、次に見ていきましょう。

整数オーバーフローを検出・防止する3つの手法

 整数オーバーフローの検出には大きく3つのアプローチが考えられます。

アプローチ1. 事前条件テスト

 事前条件テストでは、演算子に渡すオペランドの値が整数オーバーフローを発生させない範囲に収まっていることをまず確認します。整数オーバーフローが発生する場合にはArithmeticExceptionをスローし、そうでない場合にだけ演算を行います。例えばint型同士のかけ算を安全に行うための事前条件テストは以下のように書くことができます。

static final int safeMultiply(int left, int right) throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE/right || left < Integer.MIN_VALUE/right :
      (right < -1 ? left > Integer.MIN_VALUE/right || left < Integer.MAX_VALUE/right :
      right == -1 && left == Integer.Min_VALUE) ) {
    throw new ArithmeticException("Integer overflow");
  }
  return left * right;
}

 事前条件テストを行うコードは非常に煩雑になるため、一般にアップキャストを行う方がコードを簡潔に書けるでしょう。

アプローチ2. アップキャスト

 アップキャストでは演算をより大きな型で行い、オーバーフローの発生を防止します。以下にアップキャストを行うコード例を示します。

public static long intRangeCheck(long value) throws ArithmeticException {
  if ((value < Integer.MIN_VALUE) || (value > Integer.MAX_VALUE)) {
    throw new ArithmeticException("Integer overflow");
  }
  return value;
}

public static int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException {
  final long res =
    intRangeCheck(((long) oldAcc) + intRangeCheck((long) newVal * (long) scale));
  return (int) res; // safe down-cast
}

 行いたい算術演算は、oldAcc+newVal×scaleです。これを正しくアップキャストし、演算結果の範囲をチェックするために、intRangeCheck()メソッドを部分式ごとに適用しています。まず newVal×scaleの演算をlong型で行い、その結果の値がint型で表現できる範囲に収まることを確認します。次にoldAccを加算した結果がint型で表現できる範囲に収まることを確認しています。ここまで例外がスローされずに実行できれば、演算結果を安全にダウンキャストし、intの値としてreturnすることができます。

 ただし、long 型はJavaのプリミティブ整数型のうち最も大きな型なので、long型の演算はそれ以上アップキャストすることはできません。そのような場合には、アプローチ3で紹介するBigIntegerを使う必要があります。

アプローチ3. BigInteger の使用

 オペランドをBigIntegerクラスのオブジェクトに変換し、すべての算術演算をBigIntegerクラスが提供するメソッドを使って行います。BigIntegerを使った演算は整数オーバーフローしないことが保証されており、long型同士あるいはより大きな値の演算する場合に有効です。BigIntegerを使ってオーバーフローを検出するコードは以下のように書くことができます。

private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE);
private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE);

public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException {
  if (val.compareTo(bigMaxInt) == 1 ||
    val.compareTo(bigMinInt) == -1) {
    throw new ArithmeticException("Integer overflow");
  }
  return val;
}

public static int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException {
  BigInteger product =
    BigInteger.valueOf(newVal).multiply(BigInteger.valueOf(scale));
  BigInteger res = intRangeCheck(BigInteger.valueOf(oldAcc).add(product));
  return res.intValue(); // safe conversion
}

 それでは次のページで、実際のAndroidアプリケーションで整数オーバーフローが問題(プログラムが起動できない)につながった例を見てみましょう。

次のページ
mezzofantiにおける整数オーバーフローの問題

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Javaセキュアコーディング入門連載記事一覧

もっと読む

この記事の著者

久保 正樹(JPCERT コーディネーションセンター)(クボ マサキ(JPCERT コーディネーションセンター))

脆弱性アナリストJPCERTコーディネーションセンター慶応義塾大学環境情報学部卒。ソニーでデスクトップPCのソフトウェア開発に携わったのち、米国ダートマス大学にてオーディオ信号処理、電子音響音楽の研究を行い、電子音響音楽修士を取得。2005年4月よりJPCERTコーディネーションセンターにて、脆弱性...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6309 2011/12/16 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング