400 likes | 643 Views
Java への変換による 安全な C 言語の実装. 上嶋 祐紀 住井 英二郎 (東北大学 大学院 情報科学研究科). 背景. C 言語ではメモリ安全性が保証されていない 予期せぬ動作やセキュリティーホールの原因 Java 言語はメモリ安全とされている. 目的. C 言語のプログラムを Java 言語のプログラム に変換 (そのためのトランスレータを実装). C プログラムのメモリ安全性を保証 ( Java 処理系のメモリ安全性を仮定すれば). + C プログラムを Java バイトコードで配布したり、 ブラウザ上でアプレットとして動かすことも可能に.
E N D
Javaへの変換による安全なC言語の実装 上嶋 祐紀 住井 英二郎 (東北大学 大学院 情報科学研究科)
背景 • C言語ではメモリ安全性が保証されていない • 予期せぬ動作やセキュリティーホールの原因 • Java言語はメモリ安全とされている
目的 C言語のプログラムをJava言語のプログラムに変換 (そのためのトランスレータを実装) Cプログラムのメモリ安全性を保証 (Java処理系のメモリ安全性を仮定すれば) + CプログラムをJavaバイトコードで配布したり、 ブラウザ上でアプレットとして動かすことも可能に メモリ安全な言語への変換 • 仮にトランスレータにバグがあっても、 実行系が保証する安全性を確保できる
方針 • C言語の仕様で定義されている動作: Javaプログラムにおいて安全に模倣 • C言語の仕様で「未定義」とされている動作: 実行時検査により確実にエラーとする あるいは 安全な(未定義でない)動作で置き換える
実現方法 Cのメモリモデルとポインタ演算をJavaで模倣 • メモリブロック(連続したメモリ領域) • Javaの配列により模倣 • 「どのように読み書きするべきか」を表す アクセスメソッド[大岩 04] を持たせる • Blockクラスとして実装 • ポインタ • 「ベース」と「オフセット」の2ワードで表現(Fat Pointer [Austin 他 94] [大岩 他 01] ) • FatPtrクラスとして実装 型の異なるポインタ間のキャストもサポート
Outline • 変換の例 • 配列の宣言 • アドレス演算 • ポインタと整数の加算 • キャスト • 変換に利用するJavaクラスの詳細 • 最適化 • 実験結果と考察 • 関連研究 • 結論と今後の課題
変換に利用するJavaのクラス (詳細は後述) • 1ワードメモリブロック : FatBlockクラス • フィールド : contents • メソッド : アクセスメソッド • readFat : 1ワード読み出し • writeFat : 1ワード書き込み • readByte : 1バイト読み出し • … • ポインタ : FatPtrクラス • フィールド • base : FatBlockオブジェクトへの参照 • offset : 現在指しているメモリが baseから何バイト離れているかを表す整数 readFat … contents base offset
変換の例 : 配列の宣言 int i;int *p; int a[3]; 配列以外の変数は、 1要素の配列とみなす FatBlock i = new FatBlock(1);FatBlock p = new FatBlock(1); FatBlock a = new FatBlock(3); writeFat … readFat … p i offset 0 offset 0 readFat … a 0 4 8
4 変換の例 : アドレス演算 p = &a[1]; p.writeFat(0* 4, new FatPtr(a, 1 * 4)); base offset writeFat … p offset 0 readFat … a 0 4 8
4 4 変換の例 : ポインタと整数の加算 p + 1 FatPtr temp = p.readFat(0* 4); new FatPtr(temp.base,temp.offset + 1 * 4) temp readFat … p offset 0 readFat … a 0 4 8
8 4 変換の例 : ポインタと整数の加算 p + 1 FatPtr temp = p.readFat(0 * 4); new FatPtr(temp.base,temp.offset + 1 * 4) temp readFat … p offset 0 readFat … a 0 4 8
4 4 変換の例 : キャスト *(char *)p// ただし、a[1]にはintが格納されているものとする FatPtr temp = p.readFat(0* 4); temp.base.readByte(temp.offset) intを模倣するJavaオブジェクト(後述) temp readFat … p offset 0 readByte … 0x12345678 a 0 4 8
4 4 変換の例 : キャスト 実行時に何もしない (ポインタの中身は変わらない) *(char *)p FatPtr temp = p.readFat(0 * 4); temp.base.readByte(temp.offset) 1バイトの整数を模倣する Javaオブジェクト(Byteオブジェクト) temp readFat … 0x12 p offset 4 offset 0 readByte … 0x12345678 a 0 4 8
Outline • 変換の例 • 変換に利用するJavaクラスの詳細 • Block抽象クラス • FatBlockクラス • FatPtrクラス • FatIntクラス • Fat抽象クラス • 最適化 • 実験結果と考察 • 関連研究 • 結論と今後の課題
Block抽象クラス • メモリブロックをJavaで実装 • 格納される要素の型に応じた子クラスを持つ • FatBlock, ByteBlock, … Block抽象クラス FatBlockクラス ByteBlockクラス ・・・
Block抽象クラス abstract class Block { int objsize; // 格納されている要素の(Cにおける)サイズ int size; // メモリブロックのサイズ int addr; // メモリブロックの先頭アドレス Object[] contents; // 連続したメモリ領域を表現 abstract Fat readFat(int vo); // 1ワード読み出し abstract void writeFat(int vo, Fat f); // 1ワード書き込み … // 各型の値を読み書きするためのアクセスメソッド }
FatBlockクラス class FatBlock extends Block { Fat[] contents; // Fatは後述 FatBlock(int n) { objsize = 4; size = 4 * n; contents = new Fat[n]; addr = AddrCounter.getAddr(); AddrCounter.incrAddr(size); }// AddrCounterは、「現在のヒープポインタ」を模倣 Fat readFat(int vo) {//1ワード読み出し用アクセスメソッド if(vo % 4 == 0) return this.contents[vo / 4]; else // 例外を発生 } … // 各型の値を読み書きするためのアクセスメソッド }
Fat関連のクラス • FatPtrクラス: Fat Pointer [Austin 他 94] [大岩 他 01] をJavaで実装 • FatIntクラス: Fat Integer [大岩 他 01] をJavaで実装 • Fat抽象クラス: FatPtrとFatIntに共通する親クラス Fat抽象クラス FatPtrクラス FatIntクラス
FatPtrクラス class FatPtr extends Fat { /* Block base; int offset; */ FatPtr(Block b, int n) { base = b; offset = n; } int asInt() { // ポインタを整数として表示したときの値を返す if(this.base == null) return this.offset; else return this.base.addr + this.offset; } }
FatIntクラス Cでは整数とポインタを相互にキャストすることが可能: 整数もポインタと同様に表現しなければならない • Fat Integer [大岩 他 01] をJavaで実装 • C言語の整数も2ワードで表現する • FatPtrと同じフィールド、メソッドを持つ • baseは常にnull
Fat抽象クラス FatPtrとFatIntに共通する親クラス • 整数とポインタ間のキャストは何もしなくてよい abstract class Fat { Block base; int offset; abstract int asInt(); }
Outline • 変換の例 • 変換に利用するJavaクラスの詳細 • 最適化 • 最適化以前 • データフロー解析による最適化 • 実験結果と考察 • 関連研究 • 結論と今後の課題
最適化以前 • Cの基本型の変数 • 全てJavaのBlockクラスの(サブクラスの)オブジェクトに変換、 読み書きのたびにアクセスメソッドを使用 • Cの基本型の値 • 全てJavaのオブジェクトに変換 (Cの通常の整数は、ポインタとまったく関係のないものも 全てFatIntオブジェクトに変換) オブジェクト生成やメソッド呼び出しが大量に起こる • 通常のCプログラムの実行と比べ、大きなオーバーヘッド
データフロー解析による最適化 以下の条件を全て満たすCの変数を、 (BlockやFatではなく)Javaの通常の基本型変数に変換 • int, long, doubleといった、Cの基本型を持つ • ポインタを代入されることがない • アドレス演算子(&)によりアドレスを取られることがない • 関数の仮引数ではない
更なる最適化 Cの基本型の値はJavaの(オブジェクトではなく) 基本型の値に変換、 「必要になる」までオブジェクトを生成しない • 「必要になる」とは • 配列、構造体、ないしBlockオブジェクトに変換された 変数に代入される • 関数の実引数または返り値になる これらの最適化により、プログラム実行時間の ある程度の削減に成功
Outline • 変換の例 • 変換に利用するJavaクラスの詳細 • 最適化 • 実験結果と考察 • トランスレータ • 実験結果 • 考察 • 関連研究 • 結論と今後の課題
トランスレータ • Cプログラムを入力として、Javaプログラムを出力 • Objective Camlで実装 • Cプログラムの字句・構文解析には CILライブラリ [Necula 他 02] を利用 • Javaプログラムのpretty printerには Joustライブラリ [Cooper] を利用
実験 • The Computer Language Shootout Benchmarksの 9個のCプログラムを用いた • 実験環境 • CPU : Pentium 4 2.80 GHz • メインメモリ : 2 GB • OS : Linux 2.6 • GCC : 4.0.0 –O3 オプション • JavaコンパイラおよびJava仮想マシン : JDK 1.5.0
考察 • mandelbrot, partial-sums • 最適化により、手書きの場合とほぼ同じ Javaプログラムをトランスレータが生成
考察 • binary-trees, n-body • 全プログラムにわたって構造体を用いている • 構造体のメンバが全てBlockクラスのオブジェクトと なることによるオーバーヘッドが大きい • 構造体のメンバをBlockとしない実装に変更 • ループや再帰関数の中でポインタを用いている • FatPtrクラスのオーバーヘッドがある • 更なる解析・最適化が必要 • オフセットが常に0であるようなポインタを検出し、 通常のJavaの参照に変換?
考察 • nsieve, spectral-norm, recursive, nsieve-bits, fannkuch, binary-trees, n-body • 関数の引数がBlockクラスのオブジェクトとなる • ループや再帰関数の中でアクセスされることによる オーバーヘッドが大きい • 関数定義 f(t x) {…} を f(t x’) { t x = x’; …} と書き換える • x’は(決してアドレスを取られないので) Blockとする必要がない • xを最適化することも可能(最適化の条件を満たせば)
考察 • nsieve, spectral-norm, nsieve-bits, fannkuch, n-body • 配列がBlockオブジェクトとなることによる オーバーヘッドがある • 更なる解析・最適化が必要 • アクセスメソッドを必要としない配列を検出し、 通常のJavaの配列に変換?
Outline • 変換の例 • 変換に利用するJavaクラスの詳細 • 最適化 • 実験結果と考察 • 関連研究 • CからJavaへのトランスレータ • 安全なCの処理系に関する研究 • 結論と今後の課題
関連研究 : CからJavaへのトランスレータ • Jazillian [Jazillian, Inc.] • Ephedra [Martin 他 01] • 共に、CプログラムをJava言語に移植するための 支援ツール • ポインタのキャストなど、Java言語に存在しない機能は あまりサポートしていない 我々のトランスレータ • CプログラムをJava環境で安全に実行することが目的 • アクセスメソッドを使用することにより、 キャストされたポインタによるメモリアクセスにも対応
関連研究 : 安全なCの処理系に関する研究関連研究 : 安全なCの処理系に関する研究 • CCured [Necula 他 02] • 静的解析と実行時検査により、Cプログラムを安全かつ 高速に実行するための、CからCへのトランスレータ • 出力されるコードの安全性は、 トランスレータの正当性によってのみ保証 • ポインタやキャストの実現が十分ではなく、 ポインタと整数のキャストに一部対応していない • Fail-Safe C [Oiwa 他 01] • Cプログラムを安全に実行するための、CからCへのトランスレータ 我々のトランスレータ • 出力を高水準で安全なJavaプログラムとした • Javaの安全性やクラス機構を利用して、 Fail-Safe Cの概念をより明確かつ単純に実現した
Outline • 変換の例 • 変換に利用するJavaクラスの詳細 • 最適化 • 実験結果と考察 • 関連研究 • 結論と今後の課題
結論 メモリ安全な言語(Java)への変換により、 Cプログラムのメモリ安全性を確保する手法を提案
今後の課題 • ANSI C フルセットのサポート • 符号なし整数、共用体、関数ポインタ、多次元配列、goto文 • 標準ないし既存のCライブラリへの対応 • Cプログラム(を変換したJavaプログラム)から Javaのライブラリを利用する方法についても検討 • (考察で述べた)最適化 • JITコンパイラがどのような最適化をどこまで自動的に行うか(あるいは行わないか)できるだけ具体的に調べる • 他の安全な言語(C#やMLなど)への変換 • 変換されたプログラムの性能 • C言語の仕様をどこまでサポートできるか 当座の目標 : SPEC CPUベンチマーク(の大半)の動作