Module RTLperm

An instrumented semantics of RTL which records the memory permissions on function entry.

Require Import MoreRTL.
Import Utf8.
Import Util.
Import Coqlib Maps.
Import Integers Values Registers Globalenvs Memory.
Import RTL.
Import MoreSemantics.

Unset Elimination Schemes.

Definition perm_t : Type := blockZperm_kindoption permission.

Definition mem_perm (m: mem) : perm_t :=
  λ b o k, (Mem.mem_access m) # b o k.

Definition eq3 {Α Β Γ Δ} (f g: Α → Β → Γ → Δ) : Prop :=
  ∀ x y z, f x y z = g x y z.

Notation "f =3 g" := (eq3 f g) (at level 70).

Lemma eq3_trans {Α Β Γ Δ} (f g h: Α → Β → Γ → Δ):
  f =3 gg =3 hf =3 h.
Proof.
refineHfg Hgh x y z, eq_trans (Hfg x y z) (Hgh x y z)). Qed.

Record stackframe : Type := SF {
  perm : perm_t;
  sf: RTL.stackframe }.

Inductive state : Type :=
| State (stk: list stackframe) (perm: perm_t) (fn: function) (sp: val) (pc: node) (ε: regset) (m: mem)
| Callstate (stk: list stackframe) (fd: fundef) (args: list val) (m: mem)
| Returnstate (stk: list stackframe) (res: val) (m: mem).

Definition erase_stack :=
  List.map sf.

Definition erase (s: state) : RTL.state :=
  match s with
  | State stk _ fn sp pc ε m => RTL.State (erase_stack stk) fn sp pc ε m
  | Callstate stk fd args m => RTL.Callstate (erase_stack stk) fd args m
  | Returnstate stk res m => RTL.Returnstate (erase_stack stk) res m
  end.

Definition step (ge: genv) (s: state) (tr: Events.trace) (s': state) : Prop :=
  step ge (erase s) tr (erase s') ∧
  match s, s' with
  | State stk p _ _ _ _ _, State stk' p' _ _ _ _ _ => stk' = stkp' = p
  | State stk p _ _ _ _ _, Callstate stk' _ _ _ =>
    stk' = stk
    match stk' with nil => False | s :: stk' => stk' = stks.(perm) = p end
  | Callstate stk _ _ _, Returnstate stk' _ _ => stk' = stk
  | Callstate stk _ _ _, State stk' p' _ _ _ _ m' => stk' = stkp' =3 (mem_perm m')
  | State stk _ _ _ _ _ _, Returnstate stk' _ _ => stk' = stk
  | Returnstate (s :: stk) _ _, State stk' p _ _ _ _ _ => stk' = stks.(perm) = p
  | _, _ => False
  end.

Definition initial_state (p: program) (s: state) : Prop :=
  RTL.initial_state p (erase s).
  
Definition final_state (s: state) (v: int) : Prop :=
  RTL.final_state (erase s) v.

Definition semantics (p: program) : Smallstep.semantics :=
  Smallstep.Semantics step (initial_state p) final_state (Genv.globalenv p).

Theorem erase_simulation p : Smallstep.forward_simulation (semantics p) (RTL.semantics p).
Proof.
  apply Smallstep.forward_simulation_step with
  (match_states := λ x y, y = erase x).
  - abstract reflexivity.
  - abstract (intros s1 INIT; exists (erase s1); auto).
  - abstract (intros s1 s2 r ->; auto).
  - abstract (intros s1 t s1' [STEP H] s2 ->; eauto).
Defined.

Theorem simulation p : Smallstep.forward_simulation (RTL.semantics p) (semantics p).
Proof.
  apply Smallstep.forward_simulation_step with
  (match_states := λ x y, x = erase y).
  - reflexivity.
  - intros s1 INIT. inv INIT.
    eexists (Callstate nil _ _ _). vauto.
  - intros s1 s2 r ->. exact id.
  - simpl.
    intros s1 t s1' STEP; inv STEP;
      destruct s2; try discriminate;
      intros EQ; inv EQ;
    try (eexists (State _ _ _ _ _ _ _); split; [ split | ]; vauto; fail).
    eexists (Callstate (SF _ _ :: _) _ _ _); split; [ split | ]; vauto. right; vauto.
    vauto.
    eexists (Callstate _ _ _ _); split; [ split | ]; simpl; vauto.
    eexists (Returnstate _ _ _); split; [ split | ]; vauto.
    eexists (Returnstate _ _ _); split; [ split | ]; vauto.
    destruct stk as [ | [ x y ] stk ]; simpl in *; eq_some_inv. subst.
    eexists (State _ _ _ _ _ _ _). split; vauto.
Qed.

Fixpoint stack_invariant (stk: list stackframe) (fn: function) (sp: val) (m: mem) : Prop :=
  match stk with
  | nil => True
  | SF p' (Stackframe _ fn' sp' _ _) :: stk' =>
    match sp with
    | Vptr b i =>
      ∀ m', Mem.free m b (Int.unsigned i) (fn_stacksize fn) = Some m' →
            p' =3 mem_perm m' ∧ stack_invariant stk' fn' sp' m'
    | _ => True
    end
  end.

Definition invariant s : Prop :=
  match s with
  | State stk p fn sp _ _ m => p =3 mem_perm mstack_invariant stk fn sp m
  | Returnstate (SF p (Stackframe _ fn sp _ _) :: stk) _ m => p =3 mem_perm mstack_invariant stk fn sp m
  | Callstate (SF p (Stackframe _ fn sp _ _) :: stk) _ _ m => p =3 mem_perm mstack_invariant stk fn sp m
  | _ => True
  end.

Lemma invariant_init p : initial_state pinvariant.
Proof.
now intros ι INIT; destruct ι; destruct stk; inv INIT. Qed.

Lemma mem_store_permm b o v m'} :
  Mem.store κ m b o v = Some m' →
  mem_perm m =3 mem_perm m'.
Proof.
  intros STORE.
  apply Mem.store_access in STORE.
  unfold mem_perm. congruence.
Qed.

Lemma mem_free_perm {m b lo hi m'} :
  Mem.free m b lo hi = Some m' →
  ∀ q, mem_perm q =3 mem_perm m
       ∃ q', Mem.free q b lo hi = Some q' ∧
             mem_perm q' =3 mem_perm m'.
Proof.
  clear.
  Transparent Mem.free.
  unfold Mem.free.
  Opaque Mem.free.
  case Mem.range_perm_dec; intros RANGE ?; eq_some_inv. subst m'.
  intros q Q.
  exists (Mem.unchecked_free q b lo hi).
  split.
  - case Mem.range_perm_dec. reflexivity.
    intros X; elim X; clear X.
    intros o Ho. generalize (RANGE o Ho).
    unfold Mem.perm.
    generalize (Q b o Cur).
    unfold mem_perm.
    congruence.
  - intros x y z.
    unfold mem_perm.
    simpl.
    rewrite ! Regmap.gsspec.
    case peq.
    + intros ->.
      generalize (Q b y z). unfold mem_perm. intros X. rewrite X. reflexivity.
    + intros _. exact (Q x y z).
Qed.

Lemma stack_invariant_perm stk :
  ∀ (fn : function) (sp : val) (m : mem),
    stack_invariant stk fn sp m
    ∀ m' : mem,
      mem_perm m =3 mem_perm m' →
      stack_invariant stk fn sp m'.
Proof.
  elim stk; clear stk.
  easy.
  intros [vp [? fn' sp' ? ?]] stk IH fn sp m INV m' Hm'.
  destruct sp; try easy. simpl. intros m'' FREE.
  generalize (mem_free_perm FREE _ Hm'). intros (q' & FREE' & Q').
  specialize (INV _ FREE'). destruct INV as [VP INV]. fold stack_invariant in INV.
  split. congruence.
  exact (IH _ _ _ INV _ Q').
Qed.

Lemma alloc_free_perm {m lo hi m' sp m''} :
  Mem.alloc m lo hi = (m', sp) →
  Mem.free m' sp lo hi = Some m'' →
  mem_perm m =3 mem_perm m''.
Proof.
  clear.
  Transparent Mem.alloc Mem.free.
  unfold Mem.alloc. intros H; apply pair_eq_inv in H.
  destruct H as (<- & <-).
  unfold Mem.free.
  Opaque Mem.alloc Mem.free.
  case Mem.range_perm_dec; intros RANGE H; eq_some_inv; subst.
  intros b i k.
  unfold mem_perm. simpl.
  rewrite Regmap.gsspec.
  case peq.
  + intros ->. rewrite Regmap.gss.
    rewrite Mem.nextblock_noaccess. 2: unfold Plt; Psatz.lia.
    case zle; intros LE. 2: reflexivity.
    case zlt; intros LT; reflexivity.
  + intros NE. rewrite Regmap.gso; auto.
Qed.

Lemma no_infinite_list {A: Type} {a: A} {m: list A} (P: Prop) :
  m = a :: mP.
Proof.
  elim m; clear m.
  intros; eq_some_inv.
  intros a' m IH K. apply IH. eq_some_inv. congruence.
Qed.

Definition allowed_builtin ef : bool :=
  match ef with
  | AST.EF_malloc
  | AST.EF_free
  | AST.EF_inline_asm _ _ _
    => false
  | _ => true
  end.

Definition no_fancy_builtin (p: program) : bool :=
  List.forallb
    (λ q,
     match q with
     | (_, AST.Gfun (AST.External ef)) => allowed_builtin ef
     | (_, AST.Gfun (AST.Internal fn)) =>
       PTree_Properties.for_all
         (fn_code fn)
         (λ pc i,
          match i with
          | Ibuiltin ef _ _ _ => allowed_builtin ef
          | _ => true
          end)
     | _ => true end)
    (AST.prog_defs p).

Section S.

Variable p : program.
Let ge := Genv.globalenv p.

Hypothesis external_calls_do_not_change_the_permissions :
  ∀ name sg args m tr vres m',
  Events.external_functions_sem name sg ge args m tr vres m' →
  mem_perm m =3 mem_perm m'.

Hypothesis no_external_call :
  no_fancy_builtin p = true.

Lemma invariant_step s : reachable (semantics p) sinvariant s → ∀ tr, step ge s trinvariant.
Proof.
  intros REACH INV tr s' [STEP Q].
  assert (X := exist _ s eq_refl).
  assert (X' := exist _ s' eq_refl).
  destruct s, s'; inv STEP; simpl in *; hsplit; subst; auto.
  - (* Store *)
    destruct a; simpl in *; eq_some_inv.
    generalize (mem_store_perm H14). clear H14.
    eauto using eq3_trans, stack_invariant_perm.
  - (* Built-in *)
    unfold no_fancy_builtin, allowed_builtin in no_external_call.
    rewrite forallb_forall in no_external_call.
    generalize (reachable_sim (erase_simulation p) _ REACH).
    intros (idx & s2 & REACH' & -> & ->).
    generalize (call_declared_invariant _ _ REACH').
    intros [_ (name & DECL)].
    specialize (no_external_call _ DECL). simpl in no_external_call.
    rewrite PTree_Properties.for_all_correct in no_external_call.
    specialize (no_external_call _ _ H6). simpl in no_external_call.
    destruct ef; eq_some_inv;
    try (inv H14; auto; fail);
    try (apply external_calls_do_not_change_the_permissions in H14;
         eauto using eq3_trans, stack_invariant_perm).
    + (* vstore *)
      inv H14. inv H0. auto.
      apply mem_store_perm in H2.
      eauto using eq3_trans, stack_invariant_perm.
    + (* memcpy *)
      inv H14.
      cut (mem_perm m =3 mem_perm m0).
      eauto using eq3_trans, stack_invariant_perm.
      unfold mem_perm.
      now rewrite (Mem.storebytes_access _ _ _ _ _ H9).
  - (* Call *)
    destruct Q as [ Q | Q ]. subst.
    exact (no_infinite_list _ (eq_sym H7)).
    destruct stk0. easy. hsplit. subst. simpl in *. eq_some_inv.
    destruct s. simpl in *. subst. auto.
  - (* Tail call *)
    destruct Q as [ -> | Q ].
    + destruct stk as [ | [p' [ ? fn' sp' ? ?] ] ]. easy.
      simpl in *. specialize (H _ H13). auto.
    + destruct stk0. easy. hsplit. subst.
      exact (no_infinite_list _ H7).
  - (* Return *)
    destruct stk as [ | [p' [ ? fn' sp' ? ?] ] ]. easy.
    simpl in H.
    apply H. assumption.
  - (* Function entry *)
    split. easy.
    destruct stk as [ | [p' [ ? fn' sp' ? ?] ] ]. easy.
    simpl in *. eq_some_inv. inv H0.
    clear X X' REACH.
    hsplit.
    intros m' FREE.
    cut (mem_perm m =3 mem_perm m').
    eauto using eq3_trans, stack_invariant_perm.
    eauto using alloc_free_perm.
  - (* External call; same case as Ibuiltin. *)

    destruct stk as [ | [ p' [? fn' sp' ? ?] ] ]. easy.
    hsplit.

    unfold no_fancy_builtin, allowed_builtin in no_external_call.
    rewrite forallb_forall in no_external_call.
    generalize (reachable_sim (erase_simulation p) _ REACH).
    intros (idx & s2 & REACH' & -> & ->).
    generalize (call_declared_invariant _ _ REACH').
    intros [_ (name & DECL)].
    specialize (no_external_call _ DECL). simpl in no_external_call.
    destruct ef; eq_some_inv;
    try (inv H4; auto; fail);
    try (apply external_calls_do_not_change_the_permissions in H4;
         eauto using eq3_trans, stack_invariant_perm).
    + (* vstore *)
      inv H4. inv H0. auto.
      apply mem_store_perm in H2.
      eauto using eq3_trans, stack_invariant_perm.
    + (* memcpy *)
      inv H4.
      cut (mem_perm m =3 mem_perm m0).
      eauto using eq3_trans, stack_invariant_perm.
      unfold mem_perm.
      now rewrite (Mem.storebytes_access _ _ _ _ _ H9).
      
  - (* After return *)
    destruct stk. easy. hsplit. subst.
    simpl in *. eq_some_inv.
    destruct s as [ p' sf' ]. simpl in *. subst.
    exact INV.
Qed.

Theorem consistent_permissions : MoreSemantics.invariant (semantics p) invariant.
Proof.
  apply invariant_ind.
  apply invariant_init.
  apply invariant_step.
Qed.

End S.