Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
SymbolicExecution
[go: Go Back, main page]

Library SymbolicExecution

Require Export ClassicalEpsilon.
Require Export Bool.
Require Export SetoidDec.
Require Export SMT.
Require Export Programs.
Require Import String.
Require Export List.
Require Export Outcomes.

Symbolic states


Definition sstore := StringMap.t term.
Inductive schunk := SChunk(p: pred)(ts: list term).
Definition spointsto l v := SChunk pointsto_pred [l, v].
Definition smalloc_block l v := SChunk malloc_block_pred [l, v].
Definition sheap := list schunk.
Inductive sstate := SState(pc: formula)(ss: sstore)(sh: sheap).

Definition sstore0: sstore := StringMap.empty _.
Definition sstate0 := SState f_true sstore0 nil.

Symbolic evaluation


Definition sstore_lookup s x :=
  match StringMap.find x s with Some t => t | None => t_lit 0 end.
Definition sstore_update (s: sstore) x t := StringMap.add x t s.
Fixpoint sstore_updates (s: sstore) xs ts :=
  match xs, ts with
    x::xs, t::ts => sstore_updates (sstore_update s x t) xs ts
  | _, _ => s
  end.

Fixpoint seval(s: sstore)(e: expr) :=
  match e with
    e_lit z => t_lit z
  | e_var x => sstore_lookup s x
  | e_add e1 e2 => t_add (seval s e1) (seval s e2)
  end.

Fixpoint sbeval(s: sstore)(b: bexpr) :=
  match b with
    b_eq e1 e2 => f_eq (seval s e1) (seval s e2)
  | b_lt e1 e2 => f_lt (seval s e1) (seval s e2)
  | b_not b => f_not (sbeval s b)
  end.

Symbolic outcomes


Definition soutcome A := outcome sstate A.
Definition smutatora A := sstate -> soutcome A.
Definition smutator := smutatora unit.

Symbolic basic mutators


Definition fresh_mut: smutatora term :=
  fun st => let (pc, s, h) := st in
  let t := t_symb (fresh_symbol (formula_symbols pc)) in
  single (SState (f_and pc (f_eq t t)) s h) t.

Definition assume_formula(phi: formula): smutator :=
  fun st => let (pc, s, h) := st in
  if follows pc (f_not phi) then
    oblock
  else
    single (SState (f_and pc phi) s h) tt.

Definition update_sstore(x: var)(t: term): smutator :=
  fun st => let (ph, s, h) := st in
  single (SState ph (sstore_update s x t) h) tt.

Definition update_sstore_n(xs: list var)(ts: list term): smutator :=
  fun st => let (ph, s, h) := st in
  single (SState ph (sstore_updates s xs ts) h) tt.

Definition shavoc1(x: var): smutator :=
  v <- fresh_mut;
  update_sstore x v.

Fixpoint shavoc(xs: list var): smutator :=
  match xs with
    [] => noop
  | x::xs => shavoc1 x; shavoc xs
  end.

Definition swith_store{A}(s: sstore)(op: smutatora A): smutatora A :=
  fun st => let (pc, s0, h) := st in
  bindf
    (op (SState pc s h))
    (fun a st => let (pc, _, h) := st in single (SState pc s0 h) a).

Definition swith_store'(s: sstore)(op: smutator): smutatora sstore :=
  fun st => let (pc, s0, h) := st in
  bindf
    (op (SState pc s h))
    (fun _ st => let (pc, s, h) := st in single (SState pc s0 h) s).

Definition swith_local_store(op: smutator): smutator :=
  fun st => let (pc, s0, h) := st in
  bindf
    (op (SState pc s0 h))
    (fun _ st => let (pc, s, h) := st in single (SState pc s0 h) tt).

Definition seval_mut(e: expr): smutatora term :=
  fun st => let (pc, s, h) := st in
  single st (seval s e).

Definition sevals_mut(es: list expr) :=
  fun st: sstate => let (pc, s, h) := st in
  single st (map (seval s) es).

Definition sbeval_mut(b: bexpr): smutatora formula :=
  fun st => let (pc, s, h) := st in
  single st (sbeval s b).

Definition sassume_bexpr(b: bexpr): smutator :=
  seqf (sbeval_mut b) assume_formula.

Definition sassert_bexpr(b: bexpr): smutator :=
  fun st => let (pc, s, h) := st in
  if follows pc (sbeval s b) then
    single st tt
  else
    ofail.

Definition sclear_heap: smutator :=
  fun st => let (pc, s, h) := st in
  single (SState pc s nil) tt.

Definition sleakcheck: smutator :=
  fun st => let (pc, s, h) := st in
  match h with
    nil => oblock
  | SChunk p _::_ => message ("Leaking chunk " ++ p) ofail
  end.

Fixpoint extract0{A}{B}(f: A -> option B)(todo done: list A): option (list A * B) :=
  match todo with
    nil => None
  | x::xs =>
    match f x with
      None => extract0 f xs (x::done)
    | Some y => Some (done ++ xs, y)
    end
  end.

Definition extract{A B}(f: A -> option B)(xs: list A): option (list A * B) :=
  extract0 f xs nil.

Fixpoint forall2b{A B}(xs: list A)(ys: list B)(f: A -> B -> bool): bool :=
  match xs, ys with
    x::xs, y::ys => f x y && forall2b xs ys f
  | _, _ => true
  end.

Definition match_chunk pc p ts n (c: schunk) :=
  let (p', ts') := c in
  if string_dec p p' then
    if (length ts' == length ts + n)%nat then
      if forall2b ts (firstn (length ts) ts') (fun t t' => follows pc (f_eq t t')) then
        Some (skipn (length ts) ts')
      else
        None
    else
      None
  else
    None.

Definition extract_chunk p ts n: smutatora (list term) :=
  fun st => let (pc, s, h) := st in
  match extract (match_chunk pc p ts n) h with
    None => ofail
  | Some (h, ts') => single (SState pc s h) ts'
  end.

Definition extract_chunk_1_1 p l :=
  ts <- extract_chunk p [l] 1;
  yield (hd (t_lit 0) ts).

Definition extract_pointsto l := extract_chunk_1_1 pointsto_pred l.
Definition extract_malloc_block l := extract_chunk_1_1 malloc_block_pred l.

Fixpoint extract_pointstos l n :=
  match n with
    O => noop
  | S n =>
    extract_pointsto l;
    extract_pointstos (t_add l (t_lit 1)) n
  end.

Definition sprod_chunk(c: schunk): smutator :=
  fun st => let (pc, s, h) := st in
  single (SState pc s (c::h)) tt.

Fixpoint sprod_pointstos l n: smutator :=
  match n with
    O => noop
  | S n =>
    v <- fresh_mut;
    sprod_chunk (spointsto l v);
    sprod_pointstos (t_add l (t_lit 1)) n
  end.

Definition constant_term_value t: smutatora Z :=
  match t with
    t_lit z => yield z
  | _ => fail
  end.

Symbolic execution


Fixpoint sconsume(a: asn): smutator :=
  match a with
    BAsn b => sassert_bexpr b
  | PAsn p es xs =>
    message_mut "sconsume PAsn";
    ts <- sevals_mut es;
    ts' <- extract_chunk p ts (length xs);
    update_sstore_n xs ts'
  | IfAsn b a1 a2 =>
    fork (
      sassume_bexpr b;
      message_mut "sconsume IfAsn true";
      sconsume a1
    ) (
      sassume_bexpr (b_not b);
      message_mut "sconsume IfAsn false";
      sconsume a2
    )
  | SepAsn a1 a2 =>
    message_mut "sconsume SepAsn a1";
    sconsume a1;
    message_mut "sconsume SepAsn a2";
    sconsume a2
  end.

Fixpoint sproduce(a: asn): smutator :=
  match a with
    BAsn b => sassume_bexpr b
  | PAsn p es xs =>
    ts <- sevals_mut es;
    ts' <- iter fresh_mut (length xs);
    update_sstore_n xs ts';
    sprod_chunk (SChunk p (ts ++ ts'))
  | IfAsn b a1 a2 =>
    fork (
      sassume_bexpr b;
      sproduce a1
    ) (
      sassume_bexpr (b_not b);
      sproduce a2
    )
  | SepAsn a1 a2 =>
    sproduce a1;
    sproduce a2
  end.

Section SymbolicExecution.

Variable routine_specs: spec_table.
Variable pred_defs: pred_table.

Fixpoint s_exec(c: cmd): smutator :=
  match c with
  | Assign x e =>
    t <- seval_mut e;
    update_sstore x t
  | Malloc x n =>
    message_mut "Malloc";
    l <- fresh_mut;
    update_sstore x l;
    assume_formula (f_lt (t_lit 0) l);
    sprod_chunk (smalloc_block l (t_lit (Z.of_nat n)));
    sprod_pointstos l n
  | Free e =>
    message_mut "Free";
    tl <- seval_mut e;
    tn <- extract_malloc_block tl;
    n <- constant_term_value tn;
    extract_pointstos tl (Z.to_nat n)
  | Read x e =>
    message_mut "Read";
    t1 <- seval_mut e;
    t2 <- extract_pointsto t1;
    sprod_chunk (spointsto t1 t2);
    update_sstore x t2
  | Write e1 e2 =>
    message_mut "Write";
    t1 <- seval_mut e1;
    t2 <- seval_mut e2;
    _ <- extract_pointsto t1;
    sprod_chunk (spointsto t1 t2)
  | Call x r es =>
    message_mut "Call";
    match StringMap.find r routine_specs with
      None => fail
    | Some (RoutineSpec xs pre post) =>
      if length es == length xs then
        ts <- sevals_mut es;
        s1 <- swith_store' (sstore_updates sstore0 xs ts) (sconsume pre);
        t <- fresh_mut;
        swith_store (sstore_update s1 result t) (sproduce post);
        update_sstore x t
      else
        fail
    end
  | IfCmd b c1 c2 =>
    fork (
      sassume_bexpr b;
      message_mut "IfCmd true";
      s_exec c1
    ) (
      sassume_bexpr (b_not b);
      message_mut "IfCmd false";
      s_exec c2
    )
  | While b a c =>
    message_mut "While";
    swith_local_store (sconsume a);
    shavoc (targets c);
    fork (
      sclear_heap; (
        swith_local_store (sproduce a);
        sassume_bexpr b;
        s_exec c;
        swith_local_store (sconsume a)
      );
      sleakcheck
    ) (
      swith_local_store (sproduce a);
      sassume_bexpr (b_not b)
    )
  | Seq c1 c2 =>
    s_exec c1;
    s_exec c2
  | Open p es =>
    message_mut "Open";
    match pred_defs p with
      None => fail
    | Some (PredDef xs a) =>
      if le_dec (length es) (length xs) then
        ts1 <- sevals_mut es;
        ts2 <- extract_chunk p ts1 (length xs - length es);
        swith_store (sstore_updates sstore0 xs (ts1 ++ ts2)) (
          sproduce a
        )
      else
        fail
    end
  | Close p es =>
    message_mut "Close";
    match pred_defs p with
      None => fail
    | Some (PredDef xs a) =>
      if length es == length xs then
        ts <- sevals_mut es;
        swith_store (sstore_updates sstore0 xs ts) (
          sconsume a
        );
        sprod_chunk (SChunk p ts)
      else
        fail
    end
  | Skip =>
    message_mut "Skip"
  | Message m =>
    message_mut m
  end.

Inductive eresult A := ok | error: A -> eresult A.

Global Implicit Arguments ok [A].
Global Implicit Arguments error [A].

Fixpoint eresult_bind {A} (r: eresult A) (op: eresult A): eresult A :=
  match r with
    ok => op
  | error msg => error msg
  end.

Notation "op1 &&> op2" := (eresult_bind op1 op2) (at level 90).

Definition eresult_annotate(msg: string)(r: eresult string): eresult string :=
  match r with
    ok => ok
  | error msg' => error (msg ++ msg')%string
  end.

Fixpoint sresult_ok0{A}(prefix: string)(r: soutcome A): eresult string :=
  match r with
    single st a => ok
  | demonic (set_ n_Empty_set _) => ok
  | demonic (set_ n_bool o) => sresult_ok0 prefix (o true) '_x">&&> sresult_ok0 prefix (o false)
  | demonic _ => if excluded_middle_informative (safe r) then ok else error prefix
  | angelic (set_ n_Empty_set _) => error (prefix ++ " -> " ++ "failure")%string
  | angelic (set_ n_bool o) => error (prefix ++ " -> " ++ "angelic fork")%string
  | angelic _ => if excluded_middle_informative (safe r) then ok else error prefix
  | message msg o => sresult_ok0 (prefix ++ "; " ++ msg)%string o
  end.

Definition sresult_ok{A} := sresult_ok0 (A:=A) ""%string.

Section SRoutineDefs.

Variable rdefs: routine_table.

Definition svalid_routine r (rspec: routine_spec) :=
  eresult_annotate ("Routine " ++ r ++ ": ")%string (
  match rdefs r with
    None => error "routine not implemented"%string
  | Some (RoutineDef xs c) =>
    let (xs', pre, post) := rspec in
    sresult_ok (
      sstate0 '_x">|>
      vs <- iter fresh_mut (length xs');
      swith_store sstore0 (
        s1 <- swith_store' (sstore_updates sstore0 xs' vs) (sproduce pre);
        t <- swith_store (sstore_updates sstore0 xs vs) (s_exec c; seval_mut result);
        message_mut "Checking postcondition";
        swith_store (sstore_update s1 result t) (sconsume post)
      );
      sleakcheck
    )
  end
  ).

Fixpoint foralle{A B}(f: A -> eresult B)(xs: list A): eresult B :=
  match xs with
    nil => ok
  | x::xs => f x '_x">&&> foralle f xs
  end.

Definition svalid_routines :=
  foralle
    (fun el => svalid_routine (fst el) (snd el))
    (StringMap.elements routine_specs).

Definition svalid_command c :=
  sresult_ok (
    sstate0 '_x">|> s_exec c
  ).

Definition pdefs_check :=
  match pred_defs pointsto_pred with
    None => ok
  | _ => error "|-> should not be user-defined"%string
  end '_x">&&>
  match pred_defs malloc_block_pred with
    None => ok
  | _ => error "mb should not be user-defined"%string
  end.

Definition svalid_program c :=
  pdefs_check '_x">&&>
  svalid_routines '_x">&&>
  svalid_command c.

End SRoutineDefs.

End SymbolicExecution.

Example symbolic executions


Open Local Scope string_scope.

Local Notation l := "l".
Local Notation v := "v".
Local Notation next := "next".
Local Notation list := "list".
Local Notation mb := malloc_block_pred.
Local Notation n := "n".

Definition list_pred := "list".
Definition list_pred_def :=
  PredDef [l] (
    If l == 0 Then
      b_true
    Else (
      #mb(l, 2) &*& l '_'''_'''">|-> '_'''_'''">_ &*& '_'??'_x">(l + 1'_'??'_x">) '_'??'_x">|-> '_'??'_x">??next &*& #list(next)
    )
  ).

Local Notation a := "a".
Local Notation b := "b".
Local Notation tail := "tail".

Definition make_range_spec :=
  RoutineSpec [a, b]
    (b_true)
    (#list(result)).

Definition make_range :=
  RoutineDef [a, b] (
    If a == b Then (
      result ':=' 0
    ) Else (
      next ':=' call "make_range"(a + 1, b);
      result ':=' malloc(2); [result] ':=' a; [result + 1] ':=' next
    );
    close list(result)
  ).

Definition reverse_spec :=
  RoutineSpec [a, b]
    (#list(a) &*& #list(b))
    (#list(result)).

Definition reverse :=
  RoutineDef [a, b] (
    open list(a);
    If a == 0 Then
      result ':=' b
    Else (
      next ':=' [a + 1];
      [a + 1] ':=' b;
      close list(a);
      result ':=' call "reverse"(next, a)
    )
  ).

Definition dispose_spec :=
  RoutineSpec [a]
    (#list(a))
    (b_true).

Definition dispose :=
  RoutineDef [a] (
    open list(a);
    If a == 0 Then
      Skip
    Else (
      next ':=' [a + 1];
      free(a);
      call "dispose"(next)
    )
  ).

Definition reverse_loop_spec :=
  RoutineSpec [a]
    (#list(a))
    (#list(result)).

Definition reverse_loop :=
  RoutineDef [a] (
    close list(b);
    while ~ (a == 0) invariant #list(a) &*& #list(b) do (
      open list(a);
      tail ':=' [a + 1];
      [a + 1] ':=' b;
      b ':=' a;
      a ':=' tail;
      close list(b)
    );
    open list(a);
    result ':=' b
  ).

Definition dispose_loop :=
  RoutineDef [a] (
    while ~ (a == 0) invariant #list(a) do (
      open list(a);
      tail ':=' [a + 1];
      free(a);
      a ':=' tail
    );
    open list(a)
  ).

Definition my_rspecs :=
  StringMap.empty _
    '_x">|> StringMap.add "make_range" make_range_spec
    '_x">|> StringMap.add "reverse" reverse_spec
    '_x">|> StringMap.add "reverse_loop" reverse_loop_spec
    '_x">|> StringMap.add "dispose" dispose_spec
    '_x">|> StringMap.add "dispose_loop" dispose_spec
    .

Definition my_rdefs :=
  (fun r =>
     if string_dec r "make_range" then Some make_range else
     if string_dec r "reverse" then Some reverse else
     if string_dec r "reverse_loop" then Some reverse_loop else
     if string_dec r "dispose" then Some dispose else
     if string_dec r "dispose_loop" then Some dispose_loop else
     None).

Definition my_preddefs :=
  fun p =>
  if string_dec p list_pred then Some list_pred_def else
  None.

Definition reverse_test :=
  (
    a ':=' call "make_range"(0, 5);
    close list(0);
    b ':=' call "reverse"(a, 0);
    close list(0);
    a ':=' call "reverse"(b, 0);
    call "dispose"(a)
  )%cmd.

Goal
  svalid_command my_rspecs my_preddefs reverse_test = ok.

Goal
  svalid_routine my_rspecs my_preddefs my_rdefs "make_range" make_range_spec = ok.

Goal
  svalid_program my_rspecs my_preddefs my_rdefs reverse_test = ok.

Definition reverse_test_broken :=
  (
    a ':=' call "make_range"(0, 5);
    close list(0, 0);
    b ':=' call "reverse"(a, 0);
    close list(0, 0);
    call "reverse"(b + 10, 0)
  )%cmd.

Goal
  svalid_program my_rspecs my_preddefs my_rdefs reverse_test_broken '_x"><> ok.

Definition reverse_loop_test :=
  (
    a ':=' call "make_range"(0, 5);
    b ':=' call "reverse_loop"(a);
    call "reverse_loop"(b)
  )%cmd.

Goal
  svalid_program my_rspecs my_preddefs my_rdefs reverse_loop_test = ok.