形式検証入門形式手法・検証の特徴 形式手法・検証(formal methods/verification)...

18
形式検証入門 青木利晃 北陸先端科学技術大学院大学

Upload: others

Post on 19-Jan-2020

10 views

Category:

Documents


0 download

TRANSCRIPT

形式検証入門

青木利晃

北陸先端科学技術大学院大学

ソフトウェアの信頼性

社会システムの様々な場所にソフトウェアが組み込まれている。 バンキングシステム、航空管制、携帯電話、自動車、etc.

誤りを含むものが少なくない。 社会の混乱、莫大な損害、人的損害を引き起こす。

正しいソフトウェアを作ることが重要。

現在はテスト手法中心の開発。 それでも誤りが取り除けないなら、パラダイムシフトが必要。

形式手法/検証 数学的、論理的基盤に基づいたソフトウェア開発。

社会背景

形式手法/検証への期待。 信頼性低下に関する危機感。

信頼性保証のためのコストの増大。

経済産業省「情報システム信頼性向上に関するガイドライン」

国際標準のリリース. 機能安全: IEC61508, ISO26262

セキュリティ: ISO/IEC15408

後を絶たないソフトウェアのバグによる損失や混乱のニュース.

形式手法・検証の特徴

形式手法・検証(Formal Methods/Verification) 1950・60年代からヨーロッパを中心に研究・実践が行われている分野. 特定の手法を指すのではなく,色々な手法,技術,理論が含まれる分野のようなもの.

形式手法のうち,検証に特化したものを,形式検証と呼ぶ場合が多い.

数学や論理学に基づいて開発と検証を行う。 科学的な根拠に基づいた開発と検証。

多くのツールが整備されつつある.

難しそうに聞こえるが... 1980年代から多くの実践が報告されている. 比較的容易に使うことができるものから難しいものまで存在する.

プログラム検証

形式検証(Formal Verification) Formalは形式と訳すが,記号的にとか,堅苦しいという意味もある.

要するに記号化したもの(例えばプログラム)を対象に,記号に基づいて検証を行う.

1:count = 1;

2:ASSERT(0 < count)

3:count ++

4:ASSERT(0 < count)

プログラムの形式検証

ASSERTが成立することを検証(証明).

4の時点で0<countが成立するためには,2の時点で何が成立していれば良いか?

2の時点で 0 < count + 1が成立していれば良い(あたりまえ).

2のASSERTが0 < count + 1

を導けばよい.

0 < count 0 < count + 1

が成立しているかチェックすればよい(成立する)

プログラム検証

形式検証(Formal Verification) Formalは形式と訳すが,記号的にとか,堅苦しいという意味もある.

要するに記号化したもの(例えばプログラム)を対象に,記号に基づいて検証を行う.

1:count = 1;

2:ASSERT(0 < count)

3:count --

4:ASSERT(0 < count)

プログラムの形式検証

ASSERTが成立することを検証(証明).

4の時点で0<countが成立するためには,2の時点で何が成立していれば良いか?

2の時点で 0 < count - 1が成立していれば良い(あたりまえ).

2のASSERTが0 < count - 1を導けばよい.

0 < count 0 < count - 1

が成立しているかチェックすればよい(成立しない)

プログラム検証

形式検証(Formal Verification) Formalは形式と訳すが,記号的にとか,堅苦しいという意味もある.

要するに記号化したもの(例えばプログラム)を対象に,記号に基づいて検証を行う.

int max(int x, int y){1:int m;2: if (x < y){3: m = y;4: }else{5: m = x;6: }7: ASSERT( x <= m && y <= m);8: return m;}

プログラムの形式検証

x<=x && y<=x

x<=y && y<= y

(x<yx<=y && y<=y)&&

(x>=yx<=x && y<=x)成立する.

.....

.....

プログラム検証

ASSERT(x10 x20)y1:=x1; y2:=x2; y3:=1;

while y20 do

ASSERT(0x2 0y2 y3y1y2 = x1

x2)((if odd(y2) then y2:=y2 – 1;

y3:= y3*y1

else skip);

y1 := y1 * y1;

y2 := y2 / 2);

z:=y3

ASSERT(z=x1x2)

プログラム

推論規則により証明

.......... ..........

.....

推論規則

..........

..........

検証条件生成器

証明すべき性質(検証条件)

• 0y2 even(y2) 0y2/2

• y3y1y2=x1

x2 even(y2)

y3(y12)y2/2 = x1

x2,

......

生成規則

プログラム検証

適切に表明を与えれば,それらが成立するための条件(検証条件)を自動生成できる.

適切な表明を発見するのが難しい場合がある.

特に繰り返し文に関する表明が難しい.

ループ不変表明(Loop Invariant)

検証条件の確認や証明をする必要がある.

定理証明などの活用.

モデル検査

擬似コード。

void processA(){

while(1){

lock(mutex1);

lock(mutex2);

/* Critical Section */

unlock(mutex1);

unlock(mutex2);

}

}

void processB(){

while(1){

lock(mutex2);

lock(mutex1);

/* Critical Section */

unlock(mutex2);

unlock(mutex1);

}

}

モデル検査

void processA(){

while(1){

lock(mutex1);

lock(mutex2);

/* Critical Section */

unlock(mutex1);

unlock(mutex2);

}

}

void processB(){

while(1){

lock(mutex2);

lock(mutex1);

/* Critical Section */

unlock(mutex2);

unlock(mutex1);

}

}

数え上げ

A: lock(mutex1)→A:lock(mutex2)→

A:unlock(mutex1)→A:unlock(mutex2)

B: lock(mutex2)→B:lock(mutex1)→

B:unlock(mutex2)→B:unlock(mutex1)

A: lock(mutex1)→B:lock(mutex2)→

デッドロック

モデル検査

void processA(){

while(1){

lock(mutex1);

lock(mutex2);

/* Critical Section */

unlock(mutex1);

unlock(mutex2);

}

}

void processB(){

while(1){

lock(mutex1);

lock(mutex2);

/* Critical Section */

unlock(mutex1);

unlock(mutex2);

}

}

数え上げ

A: lock(mutex1)→A:lock(mutex2)→

A:unlock(mutex1)→A:unlock(mutex2)

B: lock(mutex1)→B:lock(mutex2)→

B:unlock(mutex1)→B:unlock(mutex2)

A: lock(mutex1)→A:lock(mutex2)→

A:unlock(mutex1)→B:lock(mutex1)→

A:unlock(mutex2)→B:lock(mutex2)

B: lock(mutex1)→B:lock(mutex2)→

B:unlock(mutex1)→A:lock(mutex1)→

B:unlock(mutex2)→A:lock(mutex2)

モデル検査

void processA(){

while(1){

lock(mutex1);

lock(mutex2);

/* Critical Section */

unlock(mutex1);

unlock(mutex2);

}

}

void processB(){

while(1){

lock(mutex2);

lock(mutex3);

/* Critical Section */

unlock(mutex2);

unlock(mutex3);

}

}

void processC(){

while(1){]

lock(mutex3);

lock(mutex4);

/* Critical Section */

unlock(mutex3);

unlock(mutex4);

}

}

void processD(){

while(1){

lock(mutex4);

lock(mutex5);

/* Critical Section */

unlock(mutex4);

unlock(mutex5);

}

}

void processE(){

while(1){]

lock(mutex5);

lock(mutex6);

/* Critical Section */

unlock(mutex5);

unlock(mutex6);

}

}

void processF(){

while(1){

lock(mutex6);

lock(mutex1);

/* Critical Section */

unlock(mutex6);

unlock(mutex1);

}

}

→デッドロックはおきるでしょうか?

形式検証の例

記述に基づいた数え上げは自動的にできる.

モデル検査:自動的に網羅的に数え上げる.

モデル検査概説

モデル検査手法:考えられる状態をすべて自動的に探索する。 有限状態で特徴づけられるプロセスの並行動作にまつわる性質など。 代表的なツール:NuSMV, Spin, LTSA, UPPAAL, etc.

モデル検査の「モデル」UMLなどの「モデル」 論理学では、与えられた性質をp、対象となる振る舞いをMとすると、「Mに対してpが成立する」ことを、「Mはpのモデル」であると呼ぶ。

モデル検査:振る舞いが記述した性質のモデルであることを検査する

性質の記述

時相論理(CTL, LTL),

表明,

到達性(デッドロック検出など), 進行性(飢餓状態検出など), ...

振る舞いの記述

状態遷移モデル,

並行プロセス, ...

(reqack),

(critical1 critical2), ...

モデル検査ツール

自動チェック

反例エラー検出OK

時相論理(Temporal Logic): 時間に関する性質を取り扱うことができる論理反例(Counter Example): エラーに導く実行方法

定理証明概説

定理証明手法:対話的に証明する。部分的に自動化を行う。 述語論理で書かれた式の正しさなど。

x y. xy yx x = y, n. (n * (n+1)) mod 2 = 0, etc.

算術計算など、いろいろなデータ型や演算を含む事実を証明することは決定不能問題。 決定不能問題:問題を解くアルゴリズムが存在しない(すなわち、自動的に問題が解けない)。

2種類のツール。 自動定理証明器(ATP, Automated Theorem Proving)

部分問題などを自動的に証明しようと試みる。 推論規則の効率的な適用と探索。

代表的なツール: E Prover, Simplify, Vampire, etc.

証明支援器(Proof Assistant/Proof Checker) 証明戦略を人間が与える。

紙の上で証明する代わりに、コンピュータ上で厳密かつ効率的に証明を行う。

代表的なツール:Isabelle, HOL, Coq, PVS, etc.

充足可能性問題

充足可能性問題:SAT 論理式(CNF –Conjunctive Normal Form)が充足可能であるかどうかを自動的に判定する。 充足可能: 論理式を真にするような変数への真偽値の割り当てがある。

SATソルバ 高速かつ効率的に充足可能性問題を解く。

100万命題変数を含む充足可能性問題が解けたという例もある。

代表的なSATソルバ:zChaff, MiniSat, etc.

算術計算には、やはり、弱い。 変数が取り得る値を命題変数で展開して取り扱うため、膨大な論理式になりがち。

SMTソルバ SMT(SAT Modulo Theory): 算術計算の部分を外部の自動定理証明システムなどを用いて解く。

代表的なSMTソルバ:Yices, Z3

まとめ

ツールやドキュメントが整備されてきたので使いやすくなってきた.

様々な形式検証手法と様々な使い方. お手軽なものから難しいものまで色々ある. 軽く使うだけから,どっぷり使いこなすまで色々考えられる.

まずは,「手法とツールがあるのだから,使わないのは損」,という考え方がお勧め. 「すべてを形式的にする!」,とこだわらない. 限界が感じられてから,別の手法を検討する.

既存の手順を破壊的に変更するのはやめた方が良い. 既存の手順+できる範囲の形式検証. 現在の品質以下にはならない. 手順を段階的に調整する.