修士研究発表 A Framework Using a Common Language to

修士研究発表
A Framework Using a Common
Language to Build Program Verifiers
for Low-Level Languages
米澤研究室 吉野 寿宏
<[email protected]>
背景

ネットワーク越しのコード配布の隆盛


ユーザーアプリ、OS のパッチ、ドライバ…
同時にセキュリティの脅威にもなっている



トロイの木馬、アプリケーションの脆弱性への攻撃
スパイウェア
どうしたらコードを信用できる/信用してもらえ
る?


信用できないものは実行しなければよいが、不便
全てのソフトウェアを自作することは、現実的でない
背景

プログラム検証



プログラムの中身を静的に検査し、安全であること
を数学的に保証する手法
特にセキュリティの分野で研究が盛んである
低級言語プログラムの検証は TCB を削減



高級言語はコンパイルしなければ実行できない
⇒ コンパイラも信頼しなければならない
アセンブラなどはコンパイラよりも小さい
型付きアセンブリ言語(TAL) などの既存研究
[1]
型付きアセンブリ言語 (TAL)

実マシンのアセンブリ言語に型システムを導入

レジスタの型・メモリの中身の型などを静的に検証
メモリ安全性(範囲外のメモリをアクセスしないこと)
などを保証

Java VM のバイトコード検証機構と類似


[1]
実マシンのアセンブリ言語(バイナリコードでもよい)を直接
検証できるため、OS パッチなどにも使用可
G. Morrisett, et al. From System F to Typed Assembly Language. In 1998 ACM
SIGPLAN-SIGACT Symposium on Principles of Programming Language.
低級言語向け検証器の問題点

アーキテクチャへの依存性が強い


種々のアーキテクチャそれぞれに派生言語
検証器を作ろうとするたびにアーキテクチャ
をモデル化する必要


検証器の実装において、大きな負担
実装の移植性が悪いことがしばしば
解決策

共通言語を固定し、その上に検証器を構築


言語の semantics を確定させる
実際の(低級)言語から共通言語に対して変換


変換が「正しければ」(後ほど解説)、変換元の
プログラムを検証しているのと等価
検証器のロジックとは独立性が確保できるとよい


新しいアーキテクチャに対応する際に検証器を弄らない
検証器を取り替えるときにマッピングルールを弄らない
検証器を分割実装する
共通言語に
変換された
プログラム
変換器
(2)
低級言語の
プログラム
検証結果
Success
/Fail
検証
ロジック
(3)
(1)
共通言語の意味論
検証器
本研究のアプローチ

3 つのステップ
1.
2.
3.
共通言語を設計
実際の低級言語から共通言語への変換器を作成
共通言語の semantics を用いて検証ロジックを
作成
本研究のアプローチ

3 つのステップ
1.
2.
3.

共通言語を設計
実際の低級言語から共通言語への変換器を作成
共通言語の semantics を用いて検証ロジックを
作成
本研究は特に、このうち 1. と 2. に着目

これまで 3. は数多く研究されてきているので、
それらの成果を応用することができると考えられる
共通言語

プログラムの変換対象となる言語


フレームワークの中核
変換元は低級言語 ⇒ 特徴を生かす


意味論は確定させる


取り扱う値は整数とポインタのみ
インタプリタも作っておく (変換の正しさの議論に用いる)
完全性より健全性を重視


行儀の悪いプログラムは記述できずともよい
意味論などは conservative に定義
共通言語
概要

レジスタ・メモリなどを持つ抽象機械を定義



レジスタ・メモリは多くのアーキテクチャにて採用
されている通り
一時的な結果を保持できる変数を導入
抽象機械の状態を変更する手続き型言語

アセンブリ言語等よりも、C などに近い記法を採用



中置演算子
if 文
制御構造としては、goto のみ
共通言語
言語構文
共通言語
制限事項

コードとデータを明確に分離



データの実行・コードの変更を禁止
命令デコードなどのフェーズを定式化する
必要がなくなり、意味論はシンプルに
コードを動的生成するプログラムは表現でき
なくなる

他の方法でも同じことが実現できるはず
(Performance Degradation を気にしなければ)
共通言語
制限事項

メモリブロック同士は離れていると仮定

通常は、範囲を超えてアクセスすると隣に
はみ出す


はみ出「せ」ないような semantics 設計



バッファオーバーフローなどで悪用されやすい
範囲を超えたアクセスはエラー
コードブロックに関しては、末尾に暗黙の jump
を入れることで fall-through を表現
メモリレイアウトについて何も仮定しない

Conservative な意味論定義
プログラム変換器

実際の低級言語を、設計した共通言語に変換



変換元としてはアセンブリ言語などを仮定
命令単位の変換アルゴリズム
変換後のプログラムは元のプログラムと同じ
動作をしなければならない


そうでなければ検証結果が信用できない
フレームワークの中でもっとも重要な位置を占める
プログラム変換器
変換アルゴリズム


アセンブリ言語の命令 =
命令種別 + 0 個以上のオペランド
命令種別 ⇔ テンプレート、
オペランド ⇔ パラメータ

mov( D , S ) { D := S ; } のようなルールによる
対応の記述



穴の開いた箇所にオペランドを与えることによって
テンプレートをインスタンス化
すべての可能性を列挙する手間が省ける
オペランド = 即値 | レジスタ | メモリ

即値以外は代入が可能 (出力引数)
プログラム変換器
変換アルゴリズム

プログラムをストリームとして扱う


命令、データ、ラベル(ブロック区切り)
Reader, Writer を渡すと Reader から読んで
変換


必ずしも 1 命令が 1 文に対応しない
複数ブロックに跨る場合も起こりうる
プログラム変換の正しさ


プログラム = 機械の状態集合上の関数
変換の正しさ =
実機械状態と共通言語の抽象機械の状態
の対応が任意のプログラムに関して保存
状態
State
変換元の
プログラム
変換後の
プログラム
状態’
State’
プログラム変換の正しさ



実機械の状態
抽象機械の状態
実機械の状態集合が抽象機械の状態集合に
埋め込まれる



= (レジスタ, メモリ)
= (レジスタ, メモリ, 変数)
抽象機械の状態の集合から実機械の状態の集合
へ total な surjection が定義できること
実機械では値として整数値しか用いないことを考え
れば直観的に明らか
状態の対応関係を定義
プログラム変換の正しさ
正しさを保証するには

実機械の状態集合は有限


したがって、全数検査すれば「理論的には」検証可能
しかし「現実的ではない」かもしれない


各命令の演算にかかわるオペランドのみを考慮すればよいの
で、各命令について 232 とか 264 とかの検査をすることに
たいていの場合、境界例となる一部について検証
すれば十分なことが多い

計算結果が大きく変わる場所

オーバーフローしたり、キャリーが出たり
フレームワークの実装

共通言語のインタプリタを定義


変換器を定義



変換の正しさを議論するため
Intel x86, SPARC の一部命令を定義
変換ルール→変換器へのコンパイラを実装
検証ロジックのインターフェイスを定義
フレームワークの実装

Java を使用


クラス継承を用いることで拡張性を確保
バイトコードの移植性が高い


Java VM さえあればどこでも動く
実行するのと同じアーキテクチャ上で検証したい

ライブラリが豊富

10K LoC 程度
結論

低級言語用のコード検証器のための
フレームワークを構築した

共通言語を設計した
プログラム変換器について議論した

Java による実装を行った
