Upload
hiroki-mizuno
View
4.900
Download
0
Embed Size (px)
Citation preview
Frama-Cによるソースコード検証
mzp / みずぴー
1
2011/05/14 Boost.勉強会 名古屋
自己紹介名前: mzp / みずぴー
名古屋でSEやっています
よく行く勉強会
> ocaml-nagoya
> ProofCafe
> 名古屋Scala勉強会
2
@mzp
ようこそ名古屋
3
最近の名古屋イベント2/26 名古屋Ruby会議 02
4
の隣りで名古屋Reject会議を開催
5るびまにも載っちゃった
の隣りで名古屋Reject会議を開催
5るびまにも載っちゃった
今日の主催者→
発表リスト@bleis『そうだ、bf処理系つくろう!もちろんSQLで!』
@sunflat『Amazon EC2にサーバを移転してみた』
@yoya『PHP: ZendEngineの話』
@terurou『CommonJSの話(予定)』
@nari3『GC本スピンオフ』
@mallowlabs『AsakusaSatteliteの紹介』
@yoshihiro503『初音ミクとCoqの類似性について』
@mzp『証明駆動開発の楽しみ』
@wof_moriguchi『F#による関数プログラミング』
@keigoi 『ocamljsかocaml androidの話(未定)』
@osiire『GADTブランチの今』
@kaizen_nagoya『プログラミング教育のXYZ』
@dico_leque 『Meta-objective Lisp』
6
発表リスト@bleis『そうだ、bf処理系つくろう!もちろんSQLで!』
@sunflat『Amazon EC2にサーバを移転してみた』
@yoya『PHP: ZendEngineの話』
@terurou『CommonJSの話(予定)』
@nari3『GC本スピンオフ』
@mallowlabs『AsakusaSatteliteの紹介』
@yoshihiro503『初音ミクとCoqの類似性について』
@mzp『証明駆動開発の楽しみ』
@wof_moriguchi『F#による関数プログラミング』
@keigoi 『ocamljsかocaml androidの話(未定)』
@osiire『GADTブランチの今』
@kaizen_nagoya『プログラミング教育のXYZ』
@dico_leque 『Meta-objective Lisp』
7
半分以上が関数型の話!
後日『RubyKaigi2011以降をどうするかの準備会議』にて
8
今日も証明の話をしてもいいよね!
9
なぜ証明が必要か
10
min()が欲しい
引数を2つ取って、小さいほうを返す
いろんな型で使いたい
例:min()が欲しい
11
← 顧客代表
簡単ですよねtemplateを使えば、特定の型に依存しない関数が定義できる
≦が定義されてれば、どんな型でもOK
> 構造的部分型(structual subtype)
12
template <class T>T min(T x, T y) { return (x <= y) ? x : y;}
もしもコンセプトが存在してたら
13
auto concept Le<class T> { bool operator<=(T, T);}
template <class T> requires Le<T>T min(T x, T y) { return x <= y ? x : y;}
テストもします
14
// intの場合BOOST_CHECK_EQUAL( min(1, 2), 1 );BOOST_CHECK_EQUAL( min(2, 1), 1 );BOOST_CHECK_EQUAL( min(3, 3), 3 );
// dobuleの場合BOOST_CHECK_EQUAL( min(1., 2.), 1. );BOOST_CHECK_EQUAL( min(2., 1.), 1. );
次は...
3引数版も欲しいなぁ
もちろん、いろんな型で使いたい
15
← 顧客代表
余裕、余裕引数を1個増やすだけ
minを再利用すれば簡単
相変らず≦が定義されてる型ならOK
16
template <class T> requires Le<T>T min3(T x, T y, T z) { return min(x, min(y, z));}
テストもします
17
// intの場合BOOST_CHECK_EQUAL( min3(1, 2,3), 1 );BOOST_CHECK_EQUAL( min3(2, 1,3), 1 );BOOST_CHECK_EQUAL( min3(3, 2,1), 1 );
// dobuleの場合BOOST_CHECK_EQUAL( min3(1., 2.,4.), 1. );BOOST_CHECK_EQUAL( min3(2., 1.,4.), 1. );
時は流れ..
18
クレーム「俺の型じゃ動かねーぞ」
19
“俺の型”: intとintの組
≦ の定義: x ≦ y かつ z ≦ w ならば (x,z) ≦ (y,w)
!"#"$
!"#%$ !%#"$
!%#%$
&'(
)*(
← 顧客代表
クレーム「俺の型じゃ動かねーぞ」
19
“俺の型”: intとintの組
≦ の定義: x ≦ y かつ z ≦ w ならば (x,z) ≦ (y,w)
!"#"$
!"#%$ !%#"$
!%#%$
&'(
)*(
最小を決めれない
← 顧客代表
何がいけなかったか「≦が定義されていればどんな型でもOK」← まちがい
実は暗黙の前提があった
> 反射律: x ≦ x
> 推移律: x ≦ y かつ y ≦ z ならば x ≦ z
↑の前提を満す型でしかテストしなかった
20
何をすればよかったか暗黙の前提を使わずに、仕様を満せるかを検証するべきだった
> 「≦がboolを返す」という前提だけで、min,min3が正しく動くかどうか
↑ができないなら前提が不足している
具体的な型を使うテストでは保証できない
21
無理だろ...
22
無理だろ...
22
テストじゃ
さあ証明の世界へ23
Frama-C[1]
24
C原語のソースコード解析器
いろんな静的解析をサポートしてる
> Value Analysis, Effects Analysis, etc
ホーア論理っぽいスタイルで関数の検証もできる
[1]http://frama-c.com/
検証の流れC → Why → いろんな検証器
> Alt-Ergo: 自動証明器 ← たいていこっち
> Coq: 対話証明器 ← デバッグに便利
> ...and more
仕様はACSLで記述する
> ACSL = ANSI/ISO C Specification Language
25
!"#$#%& '() *+,%-"./
00&/1
例: abs()の検証
absの仕様: 返り値は0以上
ACSLはコメントで記述する
26
//@ ensures \result >= 0;int abs (int i) { return i < 0 ? -i : i;}
検証してみる
27
$ frama-c -jessie abs.c
検証してみる
27
$ frama-c -jessie abs.c
検証してみる
27
$ frama-c -jessie abs.c
検証してみる
27
$ frama-c -jessie abs.c
失敗した!
検証してみる
27
$ frama-c -jessie abs.c
INT_MINの場合、仕様を満せない
失敗した!
前提の追加requiresを使って前提を追加する
引数はINT_MINよりも大きい
28
//@ requires i > -2147483648;//@ ensures \result >= 0;int abs (int i) { return i < 0 ? -i : i;
オールグリーン
29
Frama-Cを使ってC++を検証しよう
CとC++って似てる
> 多少変形すればFrama-Cで検証できるはず
今ある前提だけで仕様を満せるかを検証できる
抜けている前提を検出できる
30
述語: LE≦を表す述語を定義する → LE
> Cの関数はACSL中では使えないので。。
満す性質は別で与えるので実装は不要
31
/*@ axiomatic order { predicate LE(T x, T y);} */
Frama-Cで扱えるように変形する
min,min3はC++のコードなので、Frama-Cでは扱えない
Tは適当な型で置き換える → void*
演算子オーバーロード → 関数
> 実行するわけじゃないので、実装はどうでもいい
32
typedef void* T;bool leq(T x, T y) { return true; }
leqの仕様leqの仕様: 返り値がLEと一致する
> leq(x,y)がtrueならLE(x,y)は真
> leq(x,y)がfalseならLE(y, x)は真
33
/*@ ensures (¥result == true ==> LE(x,y)) && (¥result == false ==> LE(y,x)); */bool leq(T x, T y){ ... }
minの実装
leqを使ってminを実装する
仕様:返り値は引数x,y以下
34
/*@ ensures LE(¥result, x) && LE(¥result, y); */T min(T x,T y){ return leq(x,y) ? x : y; }
検証してみる→失敗
35
原因を追ってみる
LE(y,y)が成り立つことの検証に失敗してる
反射律が不足している36
要するにLE(y_0_0, y_0_0)
反射律を公理に追加する
37
/*@ axiomatic order { predicate LE(T x, T y); axiom refl : ¥forall T x; LE(x,x);
←全部合格する
min3の実装min3の仕様: 返り値がx,y,z以下
38
/*@ ensures LE(\result, x) && LE(\result, y) && LE(\result, z); */T min3(T x,T y,T z){ return min(x, min(y,z));
検証する
39
原因を追ってみる複雑なので、机上デバッグはつらい
> 証明器をCoqに切り換えると、対話的に証明できる → 原因を追いやすい
推移律が不足してる
40
LE(返り値, min(y,z))LE(min(y,z), y)
LE(返り値, y)
前提
ゴール
推移律を公理に追加する
41
/*@ axiomatic order { predicate LE(T x, T y); axiom refl : ¥forall T x; LE(x,x); axiom trans: \forall T x,T y,T z; LE(x,y) ==> LE(y,z) ==> LE(x,z);
←全部合格する
まとめテンプレートを使えば構造的部分型が実現できる
しかし、その関数の性質はテストでは保証しきれない
Frama-Cを使えば、構造的部分型を使った関数の検証をできる
例では、min,min3には反射律、推移律が必要なことが検出できた
42
宣伝:ProofSummit~秋の証明祭り~
9/25(日) 10:00~17:00
豆蔵トレーニングルーム@新宿
http://bit.ly/proofsummitCoq,Agda2などなど証明器の話がもりだくさん
43