スライド - 小林・住井研究室
Download
Report
Transcript スライド - 小林・住井研究室
Javaへの変換による
安全なC言語の実装
上嶋 祐紀 住井 英二郎
(東北大学 大学院 情報科学研究科)
背景
C言語ではメモリ安全性が保証されていない
予期せぬ動作やセキュリティーホールの原因
Java言語はメモリ安全とされている
2
目的
C言語のプログラムをJava言語のプログラムに変換
(そのためのトランスレータを実装)
Cプログラムのメモリ安全性を保証
(Java処理系のメモリ安全性を仮定すれば)
+ CプログラムをJavaバイトコードで配布したり、
ブラウザ上でアプレットとして動かすことも可能に
メモリ安全な言語への変換
仮にトランスレータにバグがあっても、
実行系が保証する安全性を確保できる
3
方針
C言語の仕様で定義されている動作:
Javaプログラムにおいて安全に模倣
C言語の仕様で「未定義」とされている動作:
実行時検査により確実にエラーとする
あるいは
安全な(未定義でない)動作で置き換える
4
実現方法
Cのメモリモデルとポインタ演算をJavaで模倣
メモリブロック(連続したメモリ領域)
Javaの配列により模倣
「どのように読み書きするべきか」を表す
アクセスメソッド[大岩 04] を持たせる
Blockクラスとして実装
ポインタ
「ベース」と「オフセット」の2ワードで表現
(Fat Pointer [Austin 他 94] [大岩 他 01] )
FatPtrクラスとして実装
型の異なるポインタ間のキャストもサポート
5
Outline
変換の例
配列の宣言
アドレス演算
ポインタと整数の加算
キャスト
変換に利用するJavaクラスの詳細
最適化
実験結果と考察
関連研究
結論と今後の課題
6
変換に利用するJavaのクラス (詳細は後述)
1ワードメモリブロック
readFat
フィールド : contents
…
メソッド : アクセスメソッド
readFat : 1ワード読み出し
writeFat : 1ワード書き込み
readByte : 1バイト読み出し
…
ポインタ
: FatBlockクラス
: FatPtrクラス
contents
base offset
フィールド
base : FatBlockオブジェクトへの参照
offset : 現在指しているメモリが
baseから何バイト離れているかを表す整数
7
変換の例 : 配列の宣言
int i; int *p;
int a[3];
配列以外の変数は、
1要素の配列とみなす
FatBlock i = new FatBlock(1); FatBlock p = new FatBlock(1);
FatBlock a = new FatBlock(3);
p
writeFat
…
offset 0
readFat
i
…
offset 0
a
readFat
…
0
4
8
8
変換の例 : アドレス演算
p = &a[1];
p.writeFat(0 * 4, new FatPtr(a, 1 * 4));
writeFat
p
…
offset 0
base
offset
4
a
readFat
…
0
4
8
9
変換の例 : ポインタと整数の加算
p+1
FatPtr temp = p.readFat(0 * 4);
new FatPtr(temp.base, temp.offset + 1 * 4)
temp
p
readFat
…
offset 0
4
4
a
readFat
…
0
4
8
10
変換の例 : ポインタと整数の加算
p+1
FatPtr temp = p.readFat(0 * 4);
new FatPtr(temp.base, temp.offset + 1 * 4)
temp
p
readFat
…
offset 0
4
8
a
readFat
…
0
4
8
11
変換の例 : キャスト
*(char *)p // ただし、a[1]にはintが格納されているものとする
FatPtr temp = p.readFat(0 * 4);
temp.base.readByte(temp.offset)
intを模倣するJavaオブジェクト(後述)
temp
p
readFat
…
offset 0
4
4
a
readByte
…
0x12345678
0
4
8
12
変換の例 : キャスト
実行時に何もしない
(ポインタの中身は変わらない)
*(char *)p
FatPtr temp = p.readFat(0 * 4);
temp.base.readByte(temp.offset)
1バイトの整数を模倣する
Javaオブジェクト(Byteオブジェクト)
temp
p
readFat
…
offset 0
4
4
a
readByte
…
0x12
offset 4
0x12345678
0
4
8
13
Outline
変換の例
変換に利用するJavaクラスの詳細
Block抽象クラス
FatBlockクラス
FatPtrクラス
FatIntクラス
Fat抽象クラス
最適化
実験結果と考察
関連研究
結論と今後の課題
14
Block抽象クラス
メモリブロックをJavaで実装
格納される要素の型に応じた子クラスを持つ
FatBlock, ByteBlock, …
Block抽象クラス
FatBlockクラス
ByteBlockクラス
・・・
15
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ワード書き込み
… // 各型の値を読み書きするためのアクセスメソッド
}
16
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 // 例外を発生
}
… // 各型の値を読み書きするためのアクセスメソッド
}
17
Fat関連のクラス
Fat抽象クラス
FatPtrクラス
FatIntクラス
FatPtrクラス:
Fat Pointer [Austin 他 94] [大岩 他 01] をJavaで実装
FatIntクラス:
Fat Integer [大岩 他 01] をJavaで実装
Fat抽象クラス:
FatPtrとFatIntに共通する親クラス
18
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;
}
}
19
FatIntクラス
Cでは整数とポインタを相互にキャストすることが可能:
整数もポインタと同様に表現しなければならない
→ Fat
Integer [大岩 他 01] をJavaで実装
C言語の整数も2ワードで表現する
FatPtrと同じフィールド、メソッドを持つ
baseは常にnull
20
Fat抽象クラス
FatPtrとFatIntに共通する親クラス
整数とポインタ間のキャストは何もしなくてよい
abstract class Fat {
Block base;
int offset;
abstract int asInt();
}
21
Outline
変換の例
変換に利用するJavaクラスの詳細
最適化
最適化以前
データフロー解析による最適化
実験結果と考察
関連研究
結論と今後の課題
22
最適化以前
→
→
Cの基本型の変数
全てJavaのBlockクラスの(サブクラスの)オブジェクトに変換、
読み書きのたびにアクセスメソッドを使用
Cの基本型の値
全てJavaのオブジェクトに変換
(Cの通常の整数は、ポインタとまったく関係のないものも
全てFatIntオブジェクトに変換)
オブジェクト生成やメソッド呼び出しが大量に起こる
通常のCプログラムの実行と比べ、大きなオーバーヘッド
23
データフロー解析による最適化
以下の条件を全て満たすCの変数を、
(BlockやFatではなく)Javaの通常の基本型変数に変換
int, long, doubleといった、Cの基本型を持つ
ポインタを代入されることがない
アドレス演算子(&)によりアドレスを取られることがない
関数の仮引数ではない
24
更なる最適化
Cの基本型の値はJavaの(オブジェクトではなく)
基本型の値に変換、
「必要になる」までオブジェクトを生成しない
「必要になる」とは
配列、構造体、ないしBlockオブジェクトに変換された
変数に代入される
関数の実引数または返り値になる
これらの最適化により、プログラム実行時間の
ある程度の削減に成功
25
Outline
変換の例
変換に利用するJavaクラスの詳細
最適化
実験結果と考察
トランスレータ
実験結果
考察
関連研究
結論と今後の課題
26
トランスレータ
Cプログラムを入力として、Javaプログラムを出力
Objective
Camlで実装
Cプログラムの字句・構文解析には
CILライブラリ [Necula 他 02] を利用
Javaプログラムのpretty
printerには
Joustライブラリ [Cooper] を利用
27
実験
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
28
実験結果
nsieve
ループ内で配列を用いて整数演算を行う
spectral-norm 浮動小数の配列で表された行列・ベクトルの演算を行う
mandelbrot
recursive
partial-sums
再帰関数内で整数演算あるいは浮動小数演算を行う
ループ内で浮動小数演算を行う
FSC2J (unopt) / C
FSC2J (opt) / C
65.85
1.65
1.63
recursive
5.43
1.87
mandelbrot
Java / C
23.29
3.55
3.90
spectral-norm
44.84
1.27
nsieve
26.81
57.22
3.06
27.52
49.91
70
60
50
40
30
20
10
0
ループ内で浮動小数演算を行う
partial-sums
29
実験結果
nsieve-bits
ループ内で配列を用いてビット演算を行う
fannkuch
ループ内で配列を用いて整数演算を行う
binary-trees
ループ内で構造体の確保、読み書き、解放を行う
ループ内で構造体の配列を用いて浮動小数演算を行う
n-body
90
80
1.95
binary-trees
38.68
69.67
1.35
38.99
fannkuch
42.62
2.34
10.72
nsieve-bits
50.87
17.84
0
Java / C
37.94
40
30
20
10
FSC2J (opt) / C
87.31
70
60
50
FSC2J (unopt) / C
n-body
30
考察
mandelbrot,
partial-sums
最適化により、手書きの場合とほぼ同じ
Javaプログラムをトランスレータが生成
31
考察
binary-trees,
n-body
全プログラムにわたって構造体を用いている
構造体のメンバが全てBlockクラスのオブジェクトと
なることによるオーバーヘッドが大きい
→構造体のメンバをBlockとしない実装に変更
ループや再帰関数の中でポインタを用いている
FatPtrクラスのオーバーヘッドがある
→更なる解析・最適化が必要
オフセットが常に0であるようなポインタを検出し、
通常のJavaの参照に変換?
32
考察
nsieve,
spectral-norm, recursive, nsieve-bits,
fannkuch, binary-trees, n-body
関数の引数がBlockクラスのオブジェクトとなる
ループや再帰関数の中でアクセスされることによる
オーバーヘッドが大きい
→関数定義 f(t x) {…} を f(t x’) { t x = x’; …} と書き換える
x’は(決してアドレスを取られないので)
Blockとする必要がない
xを最適化することも可能(最適化の条件を満たせば)
33
考察
nsieve,
spectral-norm, nsieve-bits,
fannkuch, n-body
配列がBlockオブジェクトとなることによる
オーバーヘッドがある
→ 更なる解析・最適化が必要
アクセスメソッドを必要としない配列を検出し、
通常のJavaの配列に変換?
34
Outline
変換の例
変換に利用するJavaクラスの詳細
最適化
実験結果と考察
関連研究
CからJavaへのトランスレータ
安全なCの処理系に関する研究
結論と今後の課題
35
関連研究 : CからJavaへのトランスレータ
Jazillian
[Jazillian, Inc.]
Ephedra [Martin 他 01]
共に、CプログラムをJava言語に移植するための
支援ツール
ポインタのキャストなど、Java言語に存在しない機能は
あまりサポートしていない
我々のトランスレータ
CプログラムをJava環境で安全に実行することが目的
アクセスメソッドを使用することにより、
キャストされたポインタによるメモリアクセスにも対応
36
関連研究 : 安全なCの処理系に関する研究
CCured [Necula 他 02]
静的解析と実行時検査により、Cプログラムを安全かつ
高速に実行するための、CからCへのトランスレータ
出力されるコードの安全性は、
トランスレータの正当性によってのみ保証
ポインタやキャストの実現が十分ではなく、
ポインタと整数のキャストに一部対応していない
Fail-Safe C [Oiwa 他 01]
Cプログラムを安全に実行するための、CからCへのトランスレータ
我々のトランスレータ
出力を高水準で安全なJavaプログラムとした
→ Javaの安全性やクラス機構を利用して、
Fail-Safe Cの概念をより明確かつ単純に実現した
37
Outline
変換の例
変換に利用するJavaクラスの詳細
最適化
実験結果と考察
関連研究
結論と今後の課題
38
結論
メモリ安全な言語(Java)への変換により、
Cプログラムのメモリ安全性を確保する手法を提案
39
今後の課題
ANSI C フルセットのサポート
符号なし整数、共用体、関数ポインタ、多次元配列、goto文
標準ないし既存のCライブラリへの対応
Cプログラム(を変換したJavaプログラム)から
Javaのライブラリを利用する方法についても検討
当座の目標 : SPEC CPU ベンチマーク(の大半)の動作
(考察で述べた)最適化
JITコンパイラがどのような最適化をどこまで自動的に行うか
(あるいは行わないか)できるだけ具体的に調べる
他の安全な言語(C#やMLなど)への変換
変換されたプログラムの性能
40
C言語の仕様をどこまでサポートできるか