Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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プログラムの検証

    View Slide

  2. Verified Software Toolchain C
    Cプログラムの関数的な正しさを証明するためのツールセットのこと
    • 分離論理に基づいたVerifiable Cと呼ばれるプログラム論理
    • VST-Floydと呼ばれる証明⾃動化システム
    • Coqによる健全性証明、プログラムについて証明したいかなる性質も、Cソース⾔語の操作セマンティクスのい
    かなる実⾏においても実際に成⽴することを保証します。この証明は、CompCertで検証された最適化Cコンパ
    イラの正しさの証明と組み合わされているので、アセンブリ⾔語プログラムの動作についても保証されます。

    View Slide

  3. 検証するCプログラム
    #include
    unsigned sumarray(unsigned a[], int n) {
    int i;
    unsigned s;
    i=0;
    s=0;
    while (is+=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

    View Slide

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

    View Slide

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

    View Slide

  6. 構文解析木の生成
    • clightgen sumarray.c
    • コマンドを実⾏して構⽂解析⽊sumarray.vを得る
    • 変換にはCompCertのフロントエンドを⽤いている
    • CompCertはC⾔語のほぼ全てに対応した、完全に検証された最適化コンパイラ,CompCertの全体はGallinaで書
    かれており、Coqの抽出機能を使って効率的なOCamlプログラムに抽出されている

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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割り当
    てされたものを加える

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. 関数仕様をまとめる
    • Definition Gprog := [sumarray_spec; main_spec].
    • GprogにはDECLAREで構築したfunspecが⼊っている
    • Vprogはグローバル変数の型指定のリストが⼊っていて、これは⾃動で計算される
    Print Vprog.
    (* = (_four, tarray tuint 4) : varspecs *)
    Print varspecs.
    (* = list (ident * type) *)
    script

    View Slide

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

    View Slide

  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 : 型コンテキスト,関数のパラメータの型とローカル変数の型を重ねたグローバル型コンテクストか
    ら計算される

    View Slide

  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

    View Slide

  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))はローカル変数の定義を⽰す

    View Slide

  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}

    View Slide

  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

    View Slide

  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を満たす」の意

    View Slide

  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

    View Slide

  25. • entailer! タクティクで証明が完了する
    • entailer!は論理的帰結を可能な限り簡約するタ
    クティクで多くの場合このタクティクで証明が完了
    する。
    … entailer!.
    script

    View Slide

  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

    View Slide

  27. • entailer! タクティクで証明が完了する
    • 型チェック条件は多くの場合entailer! タクティ
    クで証明が完了する。
    … entailer!.
    script

    View Slide

  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はループの中⾝

    View Slide

  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を⽤い
    る。

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. • 書き換えて両辺が等しいことを⽰す
    • ここまでで全てのゴールの証明が完了 … autorewrite with sublist in *⊢.
    autorewrite with sublist.
    reflexivity.
    Qed.
    script

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide