Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

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

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

  • LINEで送る
  • このエントリーをはてなブックマークに追加

 Java/Androidの世界では、バッファオーバーフローが問題になることはありませんが、整数オーバーフローには注意する必要があります。今回は整数オーバーフローとその対策について、Androidアプリケーションの実例を交えながら紹介します。

目次

はじめに

 C/C++の世界では、バッファオーバーフロー(CWE-120)は攻撃者による任意のコード実行を可能にする脅威の高い脆弱性として知られています。バッファオーバーフローに至る例の多くでは、整数の不適切な取り扱いによる整数オーバーフロー(CWE-190)が原因になっています。最初に次のコードを見てみましょう。

public static void main(String[] args) {
    long OVERFLOW = 24 * 60 * 60 * 1000 * 1000;
    System.out.println("One day is :" + OVERFLOW + " microseconds.");
  }
}

 これは、1日をマイクロ秒(マイクロ秒は100万分の1秒)に換算する計算を行うコードです。実行すると以下が出力されます。

One day is 500654080 microseconds.

 1日24時間は、24×60×60=86,400秒。1秒は1,000,000マイクロ秒なので、86,400×1,000,000=86,400,000,000が出力されるはずでした。なぜこのような結果が出力されてしまったのでしょう。

Javaと整数型

 今回問題となるのはJavaにおける整数の取り扱いです。Javaのデータ型をまとめると以下の図のようになります。

図1. Javaのデータ型
図1. Javaのデータ型

 Javaの整数型(Integral Type)には、符号付き整数型であるbyte、short、int、longと、符号なし整数型であるcharの4つの型が存在します。前述のコード

long OVERFLOW = 24 * 60 * 60 * 1000 * 1000;

では、整数リテラル同士のかけ算の結果がlong型変数に代入されています。問題はこのかけ算です。Java言語仕様§3.10.1「整数リテラル」には次のように書かれています。

整数リテラルは、ASCII文字Lまたはl(小文字のエル)が末尾にある場合、long型となり、それ以外の場合は、int型となる。

 つまり、右辺の24、60、1000といった整数はint型として解釈され、かけ算はint型の幅で演算されるということです。Javaのint型は、2の補数で表現される32ビット符号付き整数なので、表現できる値の範囲は-2,147,483,648から2,147,483,647です。一方、先ほどのかけ算の結果の値86,400,000,000は、int型の最大値を超えるため、整数オーバーフローが発生します。結果として、ラップアラウンドしたint型の値500,654,080が出力されたというわけです。

 このような単純な整数リテラル同士のかけ算であれば、オペランドの一つに接尾辞Lをつけるか、あるいはかけ算の一つをlongにキャストすることで、かけ算がlong型のビット幅で行われるようになり、オーバーフローを防ぐことができます(これを「アップキャスト」と呼びます)。

long NO_OVERFLOW = 24L * 60 * 60 * 1000 * 1000; //  あるいは (long)24 * 60 * ... 

 様々な整数型変数が混在する複雑な式になると、整数オーバーフローは発見しづらくなります。また、整数値がプログラムの外部から取得される場合、攻撃に悪用される危険もあるでしょう。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

バックナンバー

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

もっと読む

All contents copyright © 2005-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5