$30 off During Our Annual Pro Sale. View Details »

Verified Software Toolchain C ~Coqを用いたCプログラムの検証~

unisuke
December 11, 2021

Verified Software Toolchain C ~Coqを用いたCプログラムの検証~

unisuke

December 11, 2021
Tweet

More Decks by unisuke

Other Decks in Technology

Transcript

  1. Verified Software Toolchain C Coqを⽤いたCプログラムの検証

  2. Verified Software Toolchain C Cプログラムの関数的な正しさを証明するためのツールセットのこと • 分離論理に基づいたVerifiable Cと呼ばれるプログラム論理 • VST-Floydと呼ばれる証明⾃動化システム

    • Coqによる健全性証明、プログラムについて証明したいかなる性質も、Cソース⾔語の操作セマンティクスのい かなる実⾏においても実際に成⽴することを保証します。この証明は、CompCertで検証された最適化Cコンパ イラの正しさの証明と組み合わされているので、アセンブリ⾔語プログラムの動作についても保証されます。
  3. 検証するCプログラム #include <stddef.h> unsigned sumarray(unsigned a[], int n) { int

    i; unsigned s; i=0; s=0; while (i<n) { s+=a[i]; i++; } return s; } unsigned four[4] = {1,2,3,4}; int main(void) { unsigned int s; s = sumarray(four,4); return (int)s; } sumarray.v
  4. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  5. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  6. 構文解析木の生成 • clightgen sumarray.c • コマンドを実⾏して構⽂解析⽊sumarray.vを得る • 変換にはCompCertのフロントエンドを⽤いている • CompCertはC⾔語のほぼ全てに対応した、完全に検証された最適化コンパイラ,CompCertの全体はGallinaで書

    かれており、Coqの抽出機能を使って効率的なOCamlプログラムに抽出されている
  7. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  8. sumarrayの関数仕様 Definition sumarray_spec : ident × funspec := DECLARE _sumarray

    WITH a: val, sh : share, contents : list Z, size: Z PRE [ tptr tuint, tint ] PROP (readable_share sh; 0 ≤ size ≤ Int.max_signed; Forall (fun x ⇒ 0 ≤ x ≤ Int.max_unsigned) contents) PARAMS (a; Vint (Int.repr size)) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) POST [ tuint ] PROP () RETURN (Vint (Int.repr (sum_Z contents))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a).
  9. sumarrayの関数仕様 • DECLAREステートメントはident × funspec型 → 関数名と関数仕様を関連づけている Definition sumarray_spec :

    ident × funspec := DECLARE _sumarray WITH a: val, sh : share, contents : list Z, size: Z PRE [ tptr tuint, tint ] PROP (readable_share sh; 0 ≤ size ≤ Int.max_signed; Forall (fun x ⇒ 0 ≤ x ≤ Int.max_unsigned) contents) PARAMS (a; Vint (Int.repr size)) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) POST [ tuint ] PROP () RETURN (Vint (Int.repr (sum_Z contents))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a).
  10. sumarrayの関数仕様 • 関数はその前提条件と後条件によって定められる • PRE節 : 前提条件 • POST節 :

    後条件 • WITH節 : 前提条件と後条件の両⽅に出現する可能性のあるCoq値を定量化する Definition sumarray_spec : ident × funspec := DECLARE _sumarray WITH a: val, sh : share, contents : list Z, size: Z PRE [ tptr tuint, tint ] PROP (readable_share sh; 0 ≤ size ≤ Int.max_signed; Forall (fun x ⇒ 0 ≤ x ≤ Int.max_unsigned) contents) PARAMS (a; Vint (Int.repr size)) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) POST [ tuint ] PROP () RETURN (Vint (Int.repr (sum_Z contents))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a).
  11. sumarrayの関数仕様 • PRE節POST節の中⾝はどちらもPROP(P) LOCAL(Q) SEP(R)の形をとる Definition sumarray_spec : ident ×

    funspec := DECLARE _sumarray WITH a: val, sh : share, contents : list Z, size: Z PRE [ tptr tuint, tint ] PROP (readable_share sh; 0 ≤ size ≤ Int.max_signed; Forall (fun x ⇒ 0 ≤ x ≤ Int.max_unsigned) contents) PARAMS (a; Vint (Int.repr size)) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) POST [ tuint ] PROP () RETURN (Vint (Int.repr (sum_Z contents))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a).
  12. sumarrayの関数仕様 PROP(P) LOCAL(Q) SEP(R) • PROP(P) • PはCoqのPropでプログラムの状態と関係なく真 • LOCAL(Q)

    • Cプログラムのローカル変数の中⾝を記述するが、⽂脈によって異なる形をとる • 前提条件内ではPARAMS節で関数のパラメータを順に記述する • 後条件内ではRETURN節で関数の返り値を記述する • 関数本体の中ではパラメータを含むローカル変数を記述するためにLOCAL節を⽤いる • SEP(R) • Rは分離論理の空間アサーション • 後条件のSEP節では前提条件のSEP節で記述されている空間資源から解放されたものを除いてmalloc割り当 てされたものを加える
  13. sumarrayの関数仕様 • [ tuint ]unsigned int型を返すことを⽰す • PROPが空なので関数⼊⼒時に真だった事実が保持されていないとわかる • Return節からcontentsの各要素の合計を返り値とすることがわかる

    • SEPではメモリアドレスaにarray[size] of unsigned int 型のデータ構造が存在することをアクセス許可を⽰す shと共に記述している Definition sumarray_spec : ident × funspec := DECLARE _sumarray WITH a: val, sh : share, contents : list Z, size: Z PRE [ tptr tuint, tint ] PROP (readable_share sh; 0 ≤ size ≤ Int.max_signed; Forall (fun x ⇒ 0 ≤ x ≤ Int.max_unsigned) contents) PARAMS (a; Vint (Int.repr size)) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) POST [ tuint ] PROP () RETURN (Vint (Int.repr (sum_Z contents))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a).
  14. mainの関数仕様 Definition main_spec := DECLARE _main WITH gv : globals

    PRE [] main_pre prog tt gv POST [ tint ] PROP() RETURN (Vint (Int.repr (1+2+3+4))) SEP(TT).
  15. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  16. 関数仕様をまとめる • Definition Gprog := [sumarray_spec; main_spec]. • GprogにはDECLAREで構築したfunspecが⼊っている •

    Vprogはグローバル変数の型指定のリストが⼊っていて、これは⾃動で計算される Print Vprog. (* = (_four, tarray tuint 4) : varspecs *) Print varspecs. (* = list (ident * type) *) script
  17. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  18. f_sumarrayがsumarray_specを満たすことの証明 • グローバvルコンテキスト(Vprog,Gprog)において、sumarray()関数の本体であるf_sumarrayがsumarray_spec を満たすことを証明する Lemma body_sumarray: semax_body Vprog Gprog f_sumarray

    sumarray_spec. Proof. 1 goal (ID 72) ============================ semax_body Vprog Gprog f_sumarray sumarray_spec script goal • 述語semax_bodyは関数本体のホーアトリプルDelta ⊢ {Pre} c {Post}を述べている。 • Pre, Post : funspecから取られる • c : 関数本体 • Delta : 型コンテキスト,関数のパラメータの型とローカル変数の型を重ねたグローバル型コンテクストか ら計算される
  19. • start_functionタクティクで証明されるべき ホーアトリプルを表現する … start_function. 1 goal (ID 72) ============================

    semax_body Vprog Gprog f_sumarray sumarray_spec script goal 1 goal (ID 469) Espec : OracleKind a : val sh : share contents : list Z size : Z Delta_specs : Maps.PTree.t funspec Delta := abbreviate : tycontext SH : readable_share sh H : 0 <= size <= Int.max_signed H0 : Forall (fun x : Z => 0 <= x <= Int.max_unsigned) contents POSTCONDITION := abbreviate : ret_assert MORE_COMMANDS := abbreviate : statement ============================ semax Delta (PROP ( ) LOCAL (temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_i = (0); MORE_COMMANDS) POSTCONDITION goal
  20. • forwardタクティクでホーアトリプルの証明規則を 適⽤する … forward. . . . ============================ semax

    Delta (PROP ( ) LOCAL (temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_i = (0); MORE_COMMANDS) POSTCONDITION script goal . . . ============================ semax Delta (PROP ( ) LOCAL (temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_s = (0); MORE_COMMANDS) POSTCONDITION goal {P}(i=0;){Q} {Q}(...more...){R} --------------------------------- {P}(i=0;...more...){R} • 推論規則の適⽤によって証明すべきホーアトリプルが上段2つに変化。右の証明に当たって前提条件がPからQ に変化 • temp _i (Vint (Int.repr 0))はローカル変数の定義を⽰す
  21. • forwardタクティクでホーアトリプルの証明規則を 適⽤する … forward. . . . LOOP_BODY :=

    abbreviate : statement ============================ semax Delta (PROP ( ) LOCAL (temp _s (Vint (Int.repr 0)); temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (while (_i < _n) { LOOP_BODY } MORE_COMMANDS) POSTCONDITION script goal . . . ============================ semax Delta (PROP ( ) LOCAL (temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_s = (0); MORE_COMMANDS) POSTCONDITION goal {P}(s=0;){Q} {Q}(...more...){R} --------------------------------- {P}(s=0;...more...){R}
  22. • ループの評価にはforward_whileタクティクを⽤いる • forward_whileタクティクにはループ不変条件を与える • forward_whileタクティク実⾏後のゴールは以下 1. ループ全体の前提条件がループ不変条件であること 2. ループ条件式の型チェック

    3. ループの中⾝の後条件がループ不変条件であること 4. ループ不変条件がループ後のMORE_COMMANDSを証明するの に⼗分に強⼒な前提条件であること loop test loop body true false 不変条件 … forward_while (EX i: Z, PROP (0 <= i <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)). script
  23. 1. ループ全体の前提条件がループ不変条件であることの証明 goal . . . ============================ ENTAIL Delta, PROP

    ( ) LOCAL (temp _s (Vint (Int.repr 0)); temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- EX i : Z, PROP (0 <= i <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) • 証明のゴールはENTAIL Delta, P |-- Q,の形をした分離論理の論理的帰結 • ENTAIL Delta, P |-- Qは「コンテキストDeltaの元でPを満たす任意の状態がQを満たす」の意
  24. • Existsタクティクを使って量化変数の値を⽰す … Exists 0. . . . ============================ ENTAIL

    Delta, PROP ( ) LOCAL (temp _s (Vint (Int.repr 0)); temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- PROP (0 <= 0 <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr 0)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 0 contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) script goal . . . ============================ ENTAIL Delta, PROP ( ) LOCAL (temp _s (Vint (Int.repr 0)); temp _i (Vint (Int.repr 0)); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- EX i : Z, PROP (0 <= i <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) goal
  25. • entailer! タクティクで証明が完了する • entailer!は論理的帰結を可能な限り簡約するタ クティクで多くの場合このタクティクで証明が完了 する。 … entailer!. script

  26. 2. ループ条件式の型チェック goal . . . i : Z ============================

    ENTAIL Delta, PROP (0 <= i <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- tc_expr Delta (!(_i < _n))%expr • ループテスト式がクラッシュせずに評価できること、つまり参照する全ての変数が存在、初期化されているこ と、0で割り切れないことを証明する。これを型チェック条件とよび、述語をtc_expr としている。 • この場合ではループのi<nを証明する必要があるので右辺はtc_expr Delta (!(_i < _n))になっている
  27. • entailer! タクティクで証明が完了する • 型チェック条件は多くの場合entailer! タクティ クで証明が完了する。 … entailer!. script

  28. 3.ループの中⾝の後条件がループ不変条件であること goal . . . i : Z HRE :

    i < size H1 : 0 <= i <= size POSTCONDITION := abbreviate : ret_assert MORE_COMMANDS := abbreviate : statement ============================ semax Delta (PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) ((_t'1 = _a[_i]; _s = (_s + _t'1);) MORE_COMMANDS) POSTCONDITIONx • ループ不変条件が成⽴している上で、ループの中⾝を実⾏した時に後条件がループ不変条件と⼀致しているこ とを証明する • POSTCONDITIONがループ不変条件、cはループの中⾝
  29. • _t‘1 = _a[_i]の評価を進めるためにforwardタクティクがを実⾏したいが、失敗する • iが配列aの境界内にあることを先に証明する必要がある • HRE : i

    < size, H1 : 0 <= i <= size が仮定に存在しているのでZlength contents = sizeが 証明できれば⼗分。 • この証明に必要な情報はsemax goalの仮定条件内のdata_at sh (tarray tuint size) (map Vint (map Int.repr contents)) aから得られる。data_at述語は、配列の “contents ”リストが配列のサイズ と全く同じ⻑さであることを常に強制する。アサーションで前提条件の事実を⽤いるにはassert_PROPを⽤い る。
  30. • Zlength contents = sizeを⽰す … assert_PROP (Zlength contents =

    size). script goal . . . ============================ ENTAIL Delta, PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- !! (Zlength contents = size)
  31. • 証明のゴールが論理的帰結になっているので簡単に するためにentailer!タクティクを実⾏ … entailer!. . . . HRE :

    i < Zlength (map Vint (map Int.repr contents)) PNa : is_pointer_or_null a H2 : field_compatible (Tarray tuint (Zlength (map Vint (map Int.repr contents))) noattr) [] a H3 : Forall (value_fits tuint) (map Vint (map Int.repr contents)) ============================ Zlength contents = Zlength (map Vint (map Int.repr contents)) script goal . . . ============================ ENTAIL Delta, PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- !! (Zlength contents = size) goal
  32. • Zlength_map = forall (A B : Type) (f :

    A -> B) (l : list A), Zlength (map f l) = Zlength l • Zlength_mapを⽤いて書き換えると両辺が等しい ことが証明できる … rewrite Zlength_map. rewrite Zlength_map. reflexivity. script
  33. • Zlength contents = sizeが証明されたため forwardタクティクが実⾏できるようになる … forward. . .

    . ============================ semax Delta (PROP ( ) LOCAL (temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_s = (_s + _t'1); MORE_COMMANDS) POSTCONDITION script goal . . . H2 : Zlength contents = size ============================ semax Delta (PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) ((_t'1 = _a[_i]; _s = (_s + _t'1);) MORE_COMMANDS) POSTCONDITION goal
  34. • forwardタクティクでホーアトリプルの証明規則を 適⽤する … forward. . . . ============================ semax

    Delta (PROP ( ) LOCAL (temp _s (Vint(Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_i = (_i + (1));) POSTCONDITION script goal . . . ============================ semax Delta (PROP ( ) LOCAL (temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_s = (_s + _t'1); MORE_COMMANDS) POSTCONDITION goal
  35. • forwardタクティクでホーアトリプルの証明規則を 適⽤する … forward. . . . ============================ ENTAIL

    Delta, PROP ( ) LOCAL (temp _i (Vint (Int.add (Int.repr i) (Int.repr 1))); temp _s (Vint (Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- EX a0 : Z, PROP (0 <= a0 <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr a0)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 a0 contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) script goal . . . ============================ semax Delta (PROP ( ) LOCAL (temp _s (Vint(Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (_i = (_i + (1));) POSTCONDITION goal
  36. • Existsタクティクを使って量化変数の値を⽰す … Exists (i+1). . . . ============================ ENTAIL

    Delta, PROP ( ) LOCAL (temp _i (Vint (Int.add (Int.repr i) (Int.repr 1))); temp _s (Vint (Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- PROP (0 <= i + 1 <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr (i + 1))); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 (i + 1) contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) script goal . . . ============================ ENTAIL Delta, PROP ( ) LOCAL (temp _i (Vint (Int.add (Int.repr i) (Int.repr 1))); temp _s (Vint (Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- EX a0 : Z, PROP (0 <= a0 <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr a0)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 a0 contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) goal
  37. • entailer! タクティクで論理的帰結を簡約する … entailer!. script . . . ============================

    Vint (Int.repr (sum_Z (sublist 0 (i + 1) contents))) = Vint (Int.repr (sum_Z (sublist 0 i contents) + Znth i contents)) goal . . . ============================ ENTAIL Delta, PROP ( ) LOCAL (temp _i (Vint (Int.add (Int.repr i) (Int.repr 1))); temp _s (Vint (Int.add (Int.repr (sum_Z (sublist 0 i contents))) (Int.repr (Znth i contents)))); temp _t'1 (Vint (Int.repr (Znth i contents))); temp _a a; temp _n (Vint (Int.repr size))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) |-- PROP (0 <= i + 1 <= size) LOCAL (temp _a a; temp _i (Vint (Int.repr (i + 1))); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 (i + 1) contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a) goal
  38. • f_equal : forall (A B : Type) (f :

    A - > B) (x y : A), x = y -> f x = f y • f_equalを⽤いて書き換える … f_equal. f_equal. script . . . ============================ sum_Z (sublist 0 (i + 1) contents) = sum_Z (sublist 0 i contents) + Znth i contents goal . . . ============================ Vint (Int.repr (sum_Z (sublist 0 (i + 1) contents))) = Vint (Int.repr (sum_Z (sublist 0 i contents) + Znth i contents)) goal
  39. • 左辺を書き換えて右辺と等しいことを⽰す … rewrite (sublist_split 0 i (i+1) contents) by

    lia. rewrite sum_Z_app. rewrite (sublist_one i (i+1) contents) by lia. simpl. lia. script
  40. 4.ループ不変条件とループテストの否定の組み合わせを前提条件としてその後のMORE_COMMANDSを証明する goal 1 goal (ID 1818) Espec : OracleKind a

    : val sh : share contents : list Z size : Z Delta_specs : Maps.PTree.t funspec Delta := abbreviate : tycontext SH : readable_share sh H : 0 <= size <= Int.max_signed H0 : Forall (fun x : Z => 0 <= x <= Int.max_unsigned) contents POSTCONDITION := abbreviate : ret_assert i : Z HRE : i >= size H1 : 0 <= i <= size ============================ semax Delta (PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (return _s;) POSTCONDITION
  41. • return⽂は常にforwardタクティクで評価を進め られる … forward. PNa : is_pointer_or_null a H2

    : field_compatible (Tarray tuint (Zlength (map Vint (map Int.repr contents))) noattr) [] a H3 : Forall (value_fits tuint) (map Vint (map Int.repr contents)) ============================ data_at sh (tarray tuint (Zlength (map Vint (map Int.repr contents)))) (map Vint (map Int.repr contents)) a |-- !! (Vint (Int.repr (sum_Z (sublist 0 i contents))) = Vint (Int.repr (sum_Z contents))) script goal . . . ============================ semax Delta (PROP ( ) LOCAL (temp _a a; temp _i (Vint (Int.repr i)); temp _n (Vint (Int.repr size)); temp _s (Vint (Int.repr (sum_Z (sublist 0 i contents))))) SEP (data_at sh (tarray tuint size) (map Vint (map Int.repr contents)) a)) (return _s;) POSTCONDITION goal
  42. • entailer! タクティクで論理的帰結を簡約する … entailer!. script . . . ============================

    Vint (Int.repr (sum_Z (sublist 0 i contents))) = Vint (Int.repr (sum_Z contents)) goal . . . PNa : is_pointer_or_null a H2 : field_compatible (Tarray tuint (Zlength (map Vint (map Int.repr contents))) noattr) [] a H3 : Forall (value_fits tuint) (map Vint (map Int.repr contents)) ============================ data_at sh (tarray tuint (Zlength (map Vint (map Int.repr contents)))) (map Vint (map Int.repr contents)) a |-- !! (Vint (Int.repr (sum_Z (sublist 0 i contents))) = Vint (Int.repr (sum_Z contents))) goal
  43. • 書き換えて両辺が等しいことを⽰す • ここまでで全てのゴールの証明が完了 … autorewrite with sublist in *⊢.

    autorewrite with sublist. reflexivity. Qed. script
  44. f_mainがmain_specを満たすことの証明 • グローバルコンテキスト(Vprog,Gprog)において、main()関数の本体であるf_mainがmain_specを満たすことを 証明する Definition four_contents := [1; 2; 3;

    4]. Lemma body_main: semax_body Vprog Gprog f_main main_spec. Proof. start_function. forward_call (* s = sumarray(four,4); *) (gv _four, Ews, four_contents, 4). repeat constructor; computable. forward. (* return s; *) Qed. script
  45. 検証の手順 1. clightgenコマンドでCompCertで解析した構⽂解析⽊を⽣成する. 2. プログラムに存在する関数のAPI仕様を記述する 3. 関数仕様をまとめる 4. それぞれの関数がそれぞれの関数仕様を満たすことを証明する 5.

    semax_func証明で全てを結びつける
  46. 全ての関数を結びつける • Cプログラムは⼊出⼒を⾏い、外界に影響を与えることがある • この状態を記述したのがEspec(外部仕様) • sumarray関数は⼊出⼒を⾏わないために⾃明なEspecを使⽤できる • semax_prog prog

    Vprog Gprogという判定は、“varspecがVprogでfunspecがGprogのプログラムprogでは、 Gprogで⾔及されているすべての関数がその仕様を満たしている。”という意 Lemma prog_correct: semax_prog prog tt Vprog Gprog. Proof. prove_semax_prog. semax_func_cons body_sumarray. semax_func_cons body_main. Qed. script