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

Preview:

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