50
Frama-Cによる ソースコード検証 mzp / みずぴー 1 2011/05/14 Boost.勉強会 名古屋

「Frama-Cによるソースコード検証」 (mzp)

Embed Size (px)

Citation preview

Page 1: 「Frama-Cによるソースコード検証」 (mzp)

Frama-Cによるソースコード検証

mzp / みずぴー

1

2011/05/14 Boost.勉強会 名古屋

Page 2: 「Frama-Cによるソースコード検証」 (mzp)

自己紹介名前: mzp / みずぴー

名古屋でSEやっています

よく行く勉強会

> ocaml-nagoya

> ProofCafe

> 名古屋Scala勉強会

2

@mzp

Page 3: 「Frama-Cによるソースコード検証」 (mzp)

ようこそ名古屋

3

Page 4: 「Frama-Cによるソースコード検証」 (mzp)

最近の名古屋イベント2/26 名古屋Ruby会議 02

4

Page 5: 「Frama-Cによるソースコード検証」 (mzp)

の隣りで名古屋Reject会議を開催

5るびまにも載っちゃった

Page 6: 「Frama-Cによるソースコード検証」 (mzp)

の隣りで名古屋Reject会議を開催

5るびまにも載っちゃった

今日の主催者→

Page 7: 「Frama-Cによるソースコード検証」 (mzp)

発表リスト@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

Page 8: 「Frama-Cによるソースコード検証」 (mzp)

発表リスト@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

半分以上が関数型の話!

Page 9: 「Frama-Cによるソースコード検証」 (mzp)

後日『RubyKaigi2011以降をどうするかの準備会議』にて

8

Page 10: 「Frama-Cによるソースコード検証」 (mzp)

今日も証明の話をしてもいいよね!

9

Page 11: 「Frama-Cによるソースコード検証」 (mzp)

なぜ証明が必要か

10

Page 12: 「Frama-Cによるソースコード検証」 (mzp)

min()が欲しい

引数を2つ取って、小さいほうを返す

いろんな型で使いたい

例:min()が欲しい

11

← 顧客代表

Page 13: 「Frama-Cによるソースコード検証」 (mzp)

簡単ですよねtemplateを使えば、特定の型に依存しない関数が定義できる

≦が定義されてれば、どんな型でもOK

> 構造的部分型(structual subtype)

12

template <class T>T min(T x, T y) { return (x <= y) ? x : y;}

Page 14: 「Frama-Cによるソースコード検証」 (mzp)

もしもコンセプトが存在してたら

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;}

Page 15: 「Frama-Cによるソースコード検証」 (mzp)

テストもします

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. );

Page 16: 「Frama-Cによるソースコード検証」 (mzp)

次は...

3引数版も欲しいなぁ

もちろん、いろんな型で使いたい

15

← 顧客代表

Page 17: 「Frama-Cによるソースコード検証」 (mzp)

余裕、余裕引数を1個増やすだけ

minを再利用すれば簡単

相変らず≦が定義されてる型ならOK

16

template <class T> requires Le<T>T min3(T x, T y, T z) { return min(x, min(y, z));}

Page 18: 「Frama-Cによるソースコード検証」 (mzp)

テストもします

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. );

Page 19: 「Frama-Cによるソースコード検証」 (mzp)

時は流れ..

18

Page 20: 「Frama-Cによるソースコード検証」 (mzp)

クレーム「俺の型じゃ動かねーぞ」

19

“俺の型”: intとintの組

≦ の定義: x ≦ y かつ z ≦ w ならば (x,z) ≦ (y,w)

!"#"$

!"#%$ !%#"$

!%#%$

&'(

)*(

← 顧客代表

Page 21: 「Frama-Cによるソースコード検証」 (mzp)

クレーム「俺の型じゃ動かねーぞ」

19

“俺の型”: intとintの組

≦ の定義: x ≦ y かつ z ≦ w ならば (x,z) ≦ (y,w)

!"#"$

!"#%$ !%#"$

!%#%$

&'(

)*(

最小を決めれない

← 顧客代表

Page 22: 「Frama-Cによるソースコード検証」 (mzp)

何がいけなかったか「≦が定義されていればどんな型でもOK」← まちがい

実は暗黙の前提があった

> 反射律: x ≦ x

> 推移律: x ≦ y かつ y ≦ z ならば x ≦ z

↑の前提を満す型でしかテストしなかった

20

Page 23: 「Frama-Cによるソースコード検証」 (mzp)

何をすればよかったか暗黙の前提を使わずに、仕様を満せるかを検証するべきだった

> 「≦がboolを返す」という前提だけで、min,min3が正しく動くかどうか

↑ができないなら前提が不足している

具体的な型を使うテストでは保証できない

21

Page 24: 「Frama-Cによるソースコード検証」 (mzp)

無理だろ...

22

Page 25: 「Frama-Cによるソースコード検証」 (mzp)

無理だろ...

22

テストじゃ

Page 26: 「Frama-Cによるソースコード検証」 (mzp)

さあ証明の世界へ23

Page 27: 「Frama-Cによるソースコード検証」 (mzp)

Frama-C[1]

24

C原語のソースコード解析器

いろんな静的解析をサポートしてる

> Value Analysis, Effects Analysis, etc

ホーア論理っぽいスタイルで関数の検証もできる

[1]http://frama-c.com/

Page 28: 「Frama-Cによるソースコード検証」 (mzp)

検証の流れC → Why → いろんな検証器

> Alt-Ergo: 自動証明器 ← たいていこっち

> Coq: 対話証明器 ← デバッグに便利

> ...and more

仕様はACSLで記述する

> ACSL = ANSI/ISO C Specification Language

25

!"#$#%& '() *+,%-"./

00&/1

Page 29: 「Frama-Cによるソースコード検証」 (mzp)

例: abs()の検証

absの仕様: 返り値は0以上

ACSLはコメントで記述する

26

//@ ensures \result >= 0;int abs (int i) { return i < 0 ? -i : i;}

Page 30: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる

27

$ frama-c -jessie abs.c

Page 31: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる

27

$ frama-c -jessie abs.c

Page 32: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる

27

$ frama-c -jessie abs.c

Page 33: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる

27

$ frama-c -jessie abs.c

失敗した!

Page 34: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる

27

$ frama-c -jessie abs.c

INT_MINの場合、仕様を満せない

失敗した!

Page 35: 「Frama-Cによるソースコード検証」 (mzp)

前提の追加requiresを使って前提を追加する

引数はINT_MINよりも大きい

28

//@ requires i > -2147483648;//@ ensures \result >= 0;int abs (int i) { return i < 0 ? -i : i;

Page 36: 「Frama-Cによるソースコード検証」 (mzp)

オールグリーン

29

Page 37: 「Frama-Cによるソースコード検証」 (mzp)

Frama-Cを使ってC++を検証しよう

CとC++って似てる

> 多少変形すればFrama-Cで検証できるはず

今ある前提だけで仕様を満せるかを検証できる

抜けている前提を検出できる

30

Page 38: 「Frama-Cによるソースコード検証」 (mzp)

述語: LE≦を表す述語を定義する → LE

> Cの関数はACSL中では使えないので。。

満す性質は別で与えるので実装は不要

31

/*@ axiomatic order { predicate LE(T x, T y);} */

Page 39: 「Frama-Cによるソースコード検証」 (mzp)

Frama-Cで扱えるように変形する

min,min3はC++のコードなので、Frama-Cでは扱えない

Tは適当な型で置き換える → void*

演算子オーバーロード → 関数

> 実行するわけじゃないので、実装はどうでもいい

32

typedef void* T;bool leq(T x, T y) { return true; }

Page 40: 「Frama-Cによるソースコード検証」 (mzp)

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){ ... }

Page 41: 「Frama-Cによるソースコード検証」 (mzp)

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; }

Page 42: 「Frama-Cによるソースコード検証」 (mzp)

検証してみる→失敗

35

Page 43: 「Frama-Cによるソースコード検証」 (mzp)

原因を追ってみる

LE(y,y)が成り立つことの検証に失敗してる

反射律が不足している36

要するにLE(y_0_0, y_0_0)

Page 44: 「Frama-Cによるソースコード検証」 (mzp)

反射律を公理に追加する

37

/*@ axiomatic order { predicate LE(T x, T y); axiom refl : ¥forall T x; LE(x,x);

←全部合格する

Page 45: 「Frama-Cによるソースコード検証」 (mzp)

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));

Page 46: 「Frama-Cによるソースコード検証」 (mzp)

検証する

39

Page 47: 「Frama-Cによるソースコード検証」 (mzp)

原因を追ってみる複雑なので、机上デバッグはつらい

> 証明器をCoqに切り換えると、対話的に証明できる → 原因を追いやすい

推移律が不足してる

40

LE(返り値, min(y,z))LE(min(y,z), y)

LE(返り値, y)

前提

ゴール

Page 48: 「Frama-Cによるソースコード検証」 (mzp)

推移律を公理に追加する

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);

←全部合格する

Page 49: 「Frama-Cによるソースコード検証」 (mzp)

まとめテンプレートを使えば構造的部分型が実現できる

しかし、その関数の性質はテストでは保証しきれない

Frama-Cを使えば、構造的部分型を使った関数の検証をできる

例では、min,min3には反射律、推移律が必要なことが検出できた

42

Page 50: 「Frama-Cによるソースコード検証」 (mzp)

宣伝:ProofSummit~秋の証明祭り~

9/25(日) 10:00~17:00

豆蔵トレーニングルーム@新宿

http://bit.ly/proofsummitCoq,Agda2などなど証明器の話がもりだくさん

43