修士研究発表 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 による実装を行った
© Copyright 2025 ExpyDoc