From bf5a8ea46cb46293b5f50b46931e185b02d323c7 Mon Sep 17 00:00:00 2001 From: Benedikt Becker Date: Wed, 19 Feb 2020 12:30:46 +0100 Subject: [PATCH 1/4] Prepare functorization of symbolic engine --- src/colis.ml | 19 ++-- src/colis.mli | 9 +- src/concrete/driver.drv | 13 ++- src/concrete/interpreter.mlw | 61 ++++++----- src/concrete/semantics.mlw | 147 +++++++++++++++++---------- src/concrete/utilities.ml | 2 +- src/symbolic/driver.drv | 9 +- src/symbolic/symbolicInterpreter.mlw | 74 ++++++++------ src/symbolic/symbolicUtility.ml | 2 +- src/symbolic/symbolicUtility.mli | 2 +- 10 files changed, 206 insertions(+), 132 deletions(-) diff --git a/src/colis.ml b/src/colis.ml index a4efd82a..a056ee83 100644 --- a/src/colis.ml +++ b/src/colis.ml @@ -14,11 +14,15 @@ end module Common = struct module Arguments = Semantics__Arguments module Behaviour = Semantics__Behaviour - module Env = Env - module Stdin = Semantics__Buffers.Stdin - module Stdout = Semantics__Buffers.Stdout module Config = Semantics__Config + module Context = Semantics__Context + module Env = Semantics__Env module Input = Semantics__Input + module InterpUtilitySpec = Semantics__InterpUtilitySpec + module Path = Semantics__Path + module Result = Semantics__Result + module Stdin = Semantics__Buffers.Stdin + module Stdout = Semantics__Buffers.Stdout end module Concrete = struct @@ -72,10 +76,10 @@ module Symbolic = struct {Semantics.filesystem; stdin=Common.Stdin.empty; stdout=Common.Stdout.empty; log=Common.Stdout.empty} let to_symbolic_state ~vars ~arguments state = - let open Semantics in let context = - let var_env = Semantics.add_var_bindings true vars Semantics.empty_var_env in - {Semantics.empty_context with arguments; var_env; cwd=[]} + let open Common.Context in + let var_env = add_var_bindings true vars empty_var_env in + {empty_context with arguments; var_env; cwd=[]} in {SymState.state; context; data=()} @@ -164,12 +168,13 @@ let colis_to_file filename colis = let run ~argument0 ?(arguments=[]) ?(vars=[]) colis = let open Common in let open Concrete in + let open Context in let input = let config = Config.({loop_limit = Infinite; stack_size = Infinite }) in Input.({ argument0; config; under_condition=false }) in let state = Interpreter.empty_state () in state.arguments := arguments; - state.var_env := Semantics.add_var_bindings true vars Semantics.empty_var_env; + state.var_env := add_var_bindings true vars empty_var_env; try Interpreter.interp_program input state colis; print_string (Stdout.all_lines !(state.stdout) |> List.rev |> String.concat "\n"); diff --git a/src/colis.mli b/src/colis.mli index b0f2107c..0117a44e 100644 --- a/src/colis.mli +++ b/src/colis.mli @@ -14,10 +14,15 @@ end module Common : sig module Arguments = Semantics__Arguments module Behaviour = Semantics__Behaviour - module Env = Env + module Config = Semantics__Config + module Context = Semantics__Context + module Env = Semantics__Env + module Input = Semantics__Input + module InterpUtilitySpec = Semantics__InterpUtilitySpec + module Path = Semantics__Path + module Result = Semantics__Result module Stdin = Semantics__Buffers.Stdin module Stdout = Semantics__Buffers.Stdout - module Input = Semantics__Input end module Concrete : sig diff --git a/src/concrete/driver.drv b/src/concrete/driver.drv index 44084ff4..955b23bd 100644 --- a/src/concrete/driver.drv +++ b/src/concrete/driver.drv @@ -10,9 +10,16 @@ module semantics.Buffers syntax val split_on_default_ifs "Str.(split (regexp \"[ \t\n]+\") %1)" end -module interpreter.Semantics - syntax val filter_var_env "Env.filter_var_env (fun v -> v.Interpreter__Semantics.exported) (fun v -> v.Interpreter__Semantics.value) %1" - syntax val interp_utility "Utilities.interp_utility %1 %2 %3" +module semantics.Path + syntax type feature "string" syntax val normalized_path_to_string "String.concat \"/\" %1" syntax val absolute_or_concat_relative "Utilities.absolute_or_concat_relative %1 %2" +end + +module semantics.Context + syntax val filter_var_env "Semantics__Context.(Env.filter_var_env (fun v -> v.exported) (fun v -> v.value) %1)" +end + +module interpreter.Interpreter + syntax val interp_utility "Utilities.interp_utility %1" end \ No newline at end of file diff --git a/src/concrete/interpreter.mlw b/src/concrete/interpreter.mlw index c839995d..03565bd9 100644 --- a/src/concrete/interpreter.mlw +++ b/src/concrete/interpreter.mlw @@ -14,16 +14,19 @@ module Semantics (* Instantiate the concrete semantics with our dummy filesystem *) clone export semantics.Semantics with - type filesystem = unit, - axiom interp_utility_extends_output + type filesystem = unit end module State use ref.Ref use list.List use string.String - use Filesystem + use semantics.Buffers + use semantics.Context + use semantics.Env + use semantics.Path + use Filesystem use import Semantics as S type sem_state = state @@ -42,7 +45,7 @@ module State var_env : ref var_env; func_env : func_env; arguments: ref (list string); - cwd : ref (list string); + cwd : ref normalized_path; result : ref bool; } end @@ -59,18 +62,22 @@ module Interpreter use map.MapExt use int.Int use ref.Refint + use string.OCaml as String use auxiliaries.OptionGet - use string.OCaml as String + use auxiliaries.TakeDrop use syntax.Syntax + use semantics.Arguments use semantics.Result use semantics.Buffers use semantics.Behaviour use semantics.Input use semantics.Config + use semantics.Context + use semantics.InterpUtilitySpec use semantics.Env as E + use semantics.Path use Filesystem as Fs - use auxiliaries.TakeDrop use import Semantics as S use import State as St @@ -85,8 +92,8 @@ module Interpreter let empty_state () = { St.filesystem = ref Filesystem.empty; - St.var_env = ref S.empty_var_env; - St.func_env = S.empty_func_env; + St.var_env = ref empty_var_env; + St.func_env = empty_func_env; St.arguments = ref Nil; St.stdin = ref Stdin.empty; St.stdout = ref Stdout.empty; @@ -95,12 +102,12 @@ module Interpreter St.result = ref True; } - function sem_context (sta:state) : S.context = { - S.var_env = !(sta.var_env); - S.func_env = sta.func_env; - S.arguments = !(sta.arguments); - S.cwd = !(sta.cwd); - S.result = !(sta.result); + function sem_context (sta:state) : context = { + Context.var_env = !(sta.var_env); + Context.func_env = sta.func_env; + Context.arguments = !(sta.arguments); + Context.cwd = !(sta.cwd); + Context.result = !(sta.result); } lemma context_same: forall sta1 sta2. @@ -139,22 +146,30 @@ module Interpreter (** {3 Imperative wrapper around the functional interpretation of utilties} *) - let interp_utility (inp:input) (sta:state) (id:identifier) (args:list string) + (** Interprete a command defined in the document *Specification of UNIX Commands*. *) + val interp_utility (params: (utility_context, identifier, S.state)) : (S.state, result bool) + ensures { S.interp_utility params result } + + let interp_utility' (inp:input) (sta:state) (id:identifier) (args:list string) ensures { - interp_utility (!(old sta.cwd), filter_var_env !(old sta.var_env), args) id (old sem_state sta) = (sem_state sta, Ok !(sta.result)) /\ + let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Ok !(sta.result)) /\ behaviour inp !(sta.result) = BNormal } raises { EExit -> - interp_utility (!(old sta.cwd), filter_var_env !(old sta.var_env), args) id (old sem_state sta) = (sem_state sta, Ok !(sta.result)) /\ + let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Ok !(sta.result)) /\ behaviour inp !(sta.result) = BExit } raises { EIncomplete -> - interp_utility (!(old sta.cwd), filter_var_env !(old sta.var_env), args) id (old sem_state sta) = (sem_state sta, Incomplete) + let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Incomplete) } raises { EIncomplete -> sem_context sta = sem_context (old sta) } - = let sta', r = interp_utility (!(sta.cwd), filter_var_env !(sta.var_env), args) id (sem_state sta) in + = let utility_ctx = (!(sta.cwd), filter_var_env !(sta.var_env), args) in + let sta', r = interp_utility (utility_ctx, id, sem_state sta) in sta.filesystem := sta'.S.filesystem; assert { !(sta.filesystem) = sta'.S.filesystem }; sta.stdin := sta'.S.stdin; @@ -168,8 +183,6 @@ module Interpreter raise EIncomplete end - use semantics.Arguments - let shift_arguments sta n requires { n >= 0 } ensures { @@ -256,7 +269,7 @@ module Interpreter | IExport id -> let old_env = !(sta.var_env) in - let var_val = {old_env[id] with S.exported=True} in + let var_val = {old_env[id] with exported=True} in sta.var_env := old_env[id <- var_val]; sta.result := True @@ -339,7 +352,7 @@ module Interpreter | ICallUtility name le -> let args = interp_list_expr inp sta le in - interp_utility inp sta name args + interp_utility' inp sta name args | ICallFunction id le -> let args = interp_list_expr inp sta le in @@ -430,7 +443,7 @@ module Interpreter let cwd_p = absolute_or_concat_relative !(sta.cwd) s in let pwd_s = normalized_path_to_string cwd_p in let args = Cons "-d" (Cons pwd_s Nil) in - interp_utility {inp with under_condition=True} sta identifier_test args; + interp_utility' {inp with under_condition=True} sta identifier_test args; if !(sta.result) then begin sta.var_env := !(sta.var_env)[identifier_pwd <- pwd_s]'; sta.cwd := cwd_p diff --git a/src/concrete/semantics.mlw b/src/concrete/semantics.mlw index d641a898..0f1d3930 100644 --- a/src/concrete/semantics.mlw +++ b/src/concrete/semantics.mlw @@ -32,20 +32,6 @@ module Behaviour | BIncomplete, BIncomplete -> True | _ -> False end - - (* let function behaviour (under_condition res:bool) : behaviour *) - (* ensures { result = BNormal \/ result = BExit } *) - (* returns { *) - (* | BNormal -> *) - (* under_condition = True \/ res = True *) - (* | BExit -> *) - (* under_condition = False /\ res = False *) - (* | BReturn | BIncomplete -> *) - (* false *) - (* } *) - (* = if andb (strict under_condition) (notb res) *) - (* then BExit *) - (* else BNormal *) end (** {2 Stdin and stdout buffers} *) @@ -159,7 +145,6 @@ module Buffers | Cons l ls -> concat_empty_left {line=l; lines=ls} end - let rec lemma concat_empty_right (out:t) variant { out.lines } ensures { @@ -252,9 +237,14 @@ end module Env + use option.Option use syntax.Identifier use map.Map as M + (** A partial environment *) + type env0 'a = abstract { to_map0 : M.map identifier (option 'a) } + + (** A total environment with default *) type env 'a = abstract { to_map : M.map identifier 'a; default: 'a } meta coercion function to_map @@ -315,26 +305,34 @@ module Arguments shift_arguments n args = None end -(** {2 Concrete semantics of the CoLiS language} *) -module Semantics +module Path + + use list.List + + (** abstract type for filenames (excluding "." and ".."), named + "feature" in the tree constraint vocabulary *) + type feature + + type normalized_path = list feature + + let constant default_cwd : normalized_path = Nil + + (** For `cd`: append `s` to `p` if `s` is relative or just `s` as path if it is absolute *) + val function absolute_or_concat_relative (p:normalized_path) (s:string) : normalized_path + + val function normalized_path_to_string (p:normalized_path) : string +end + +(** {2 Concrete evaluation context} *) +module Context - use int.Int use list.List - use list.Append use option.Option - use bool.Bool use string.String use syntax.Syntax - - use auxiliaries.OptionGet - use Result - use Arguments - use Behaviour - use Buffers - use Config - use Input - use import Env as E + use Path + use Env (** {3 The concrete evaluation context} @@ -366,16 +364,6 @@ module Semantics let constant empty_func_env : func_env = empty_env None - type normalized_path = list string - - let constant default_cwd : normalized_path = Nil - - (** For `cd` *) - (** Append `s` to `p` if `s` is relative or just `s` as path if it is absolute *) - val function absolute_or_concat_relative (p:normalized_path) (s:string) : normalized_path - - val function normalized_path_to_string (p:normalized_path) : string - let constant identifier_test = identifier_of_string "test" let constant identifier_pwd = identifier_of_string "PWD" @@ -403,12 +391,57 @@ module Semantics result = True; } - val function filter_var_env (env:var_env) : env string + val function filter_var_env (env:var_env) : env0 string (** A specification is not required because the result is not used inside the language but only in utilities which are not specified. *) (* ensures { forall x s. *) (* env[x] = {value=Some s; exported=True} <-> result[x] = s *) (* } *) +end (* Context *) + +(** Split the types to specify the interpretation of utilities to enable factorization + of the symbolic engine *) +module InterpUtilitySpec + use list.List + use syntax.Syntax + use Result + use Path + use Buffers + use Env + + type utility_context = (normalized_path (* cwd *), env0 string (* variable env *), list string (* args *)) + + type raw_state 'fs = ('fs, stdin, stdout, stdout (* log *)) + + predicate raw_interp_utility (utility_context, identifier, raw_state 'fs) (raw_state 'fs, result bool) + + axiom interp_utility_extends_output : forall ctx id stdin stdin' stdout stdout' log log' b, fs fs': 'fs. + raw_interp_utility (ctx, id, (fs, stdin, Stdout.empty, log)) ((fs', stdin', stdout', log'), b) -> + raw_interp_utility (ctx, id, (fs, stdin, stdout, log)) ((fs', stdin', Stdout.concat stdout' stdout', log), b) +end + + +module Semantics + use bool.Bool + use int.Int + use list.Append + use list.List + use option.Option + + use auxiliaries.OptionGet + use syntax.Identifier + use syntax.Syntax + + use Arguments + use Behaviour + use Buffers + use Config + use Context + use Env + use Input + use InterpUtilitySpec + use Path + use Result (** {3 Evaluation state} @@ -427,14 +460,17 @@ module Semantics log: stdout; (** Combines stdout with utility traces and error messages *) } - type utility_context = (normalized_path, env string, list string) + let function to_raw_state (s: state) : raw_state filesystem = + (s.filesystem, s.stdin, s.stdout, s.log) - (** Interprete a command defined in the document *Specification of UNIX Commands*. *) - val function interp_utility utility_context identifier state : (state, result bool) + let function of_raw_state (s0: raw_state filesystem) : state = + let (filesystem, stdin, stdout, log) = s0 in + {filesystem = filesystem; stdin = stdin; stdout = stdout; log = log} - axiom interp_utility_extends_output : forall ctx id sta sta' r. - interp_utility ctx id {sta with stdout=Stdout.empty} = (sta', r) -> - interp_utility ctx id sta = ({sta' with stdout=Stdout.concat sta.stdout sta'.stdout}, r) + predicate interp_utility (params: (utility_context, identifier, state)) (res: (state, result bool)) = + let ctx, name, sta = params in + let sta', res = res in + raw_interp_utility (ctx, name, to_raw_state sta) (to_raw_state sta', res) let function bool_of_return_code (c:return_code) (previous:bool) : bool = match c with @@ -462,10 +498,10 @@ module Semantics (* Reexport mixfix operators. *) let function ([]) (e:env 'a) (id:identifier) : 'a = - E.(e[id]) + Env.(e[id]) let function ([<-]) (e:env 'a) (id:identifier) (value:'a) : env 'a = - E.(e[id <- value]) + Env.(e[id <- value]) (** Define mixfix operators for variable environments that retain or ignore the export_state flag in the environment variable. *) @@ -523,14 +559,16 @@ module Semantics eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) let cwd_p = absolute_or_concat_relative ctx.cwd s in - interp_utility (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) identifier_test sta1 = (sta2, Incomplete) -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Incomplete) -> eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx, BIncomplete) | eval_cd_no_dir : forall stk inp ctx sta se sta1 s _b sta2. eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) let cwd_p = absolute_or_concat_relative ctx.cwd s in - interp_utility (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) identifier_test sta1 = (sta2, Ok False) -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok False) -> let ctx1 = {ctx with result = False} in let bhv = behaviour inp False in eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx1, bhv) @@ -539,7 +577,8 @@ module Semantics eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) let cwd_p = absolute_or_concat_relative ctx.cwd s in - interp_utility (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) identifier_test sta1 = (sta2, Ok True) -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok True) -> let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- normalized_path_to_string cwd_p]'; cwd = cwd_p} in let bhv = behaviour inp True in eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx1, bhv) @@ -631,12 +670,14 @@ module Semantics | eval_call_utility_incomplete: forall stk inp ctx sta sta' sta'' id es ss. eval_list_expr stk (inp, ctx, sta) es (sta', Ok ss) -> - interp_utility (ctx.cwd, filter_var_env ctx.var_env, ss) id sta' = (sta'', Incomplete) -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in + interp_utility (utility_ctx, id, sta') (sta'', Incomplete) -> eval_instruction stk (inp, ctx, sta) (ICallUtility id es) (sta'', ctx, BIncomplete) | eval_call_utility: forall stk inp ctx sta sta' sta'' id es ss b. eval_list_expr stk (inp, ctx, sta) es (sta', Ok ss) -> - interp_utility (ctx.cwd, filter_var_env ctx.var_env, ss) id sta' = (sta'', Ok b) -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in + interp_utility (utility_ctx, id, sta') (sta'', Ok b) -> let ctx' = {ctx with result=b} in let bhv = behaviour inp b in eval_instruction stk (inp, ctx, sta) (ICallUtility id es) (sta'', ctx', bhv) diff --git a/src/concrete/utilities.ml b/src/concrete/utilities.ml index 187652b2..f3d4f0e6 100644 --- a/src/concrete/utilities.ml +++ b/src/concrete/utilities.ml @@ -42,7 +42,7 @@ let dpkg_compare_versions args = let dpkg_validate_thing subcmd arg = Sys.command ("dpkg " ^ subcmd ^ " " ^arg) = 0 -let interp_utility (_cwd, var_env, args) id sta = +let interp_utility ((_cwd, var_env, args), id, sta) = match id with | "echo" -> let stdout = diff --git a/src/symbolic/driver.drv b/src/symbolic/driver.drv index 220d850d..b3b8aeee 100644 --- a/src/symbolic/driver.drv +++ b/src/symbolic/driver.drv @@ -5,15 +5,8 @@ module symbolicInterpreter.Constraints syntax type path "Colis_constraints.Path.t" end -module symbolicInterpreter.Semantics - syntax val filter_var_env "Env.filter_var_env (fun v -> v.SymbolicInterpreter__Semantics.exported) (fun v -> v.SymbolicInterpreter__Semantics.value) %1" - syntax val interp_utility "assert false" - syntax val absolute_or_concat_relative "Colis_constraints.(List.map Feat.to_string (Path.normalize ~cwd:(List.map Feat.from_string %1) (Path.from_string %2)))" - syntax val normalized_path_to_string "Colis_constraints.(Path.normal_to_string (List.map Feat.from_string %1))" -end - module symbolicInterpreter.Interpreter - syntax val sym_interp_utility "BatSet.to_list (SymbolicUtility.dispatch' %1 %2 %3)" + syntax val sym_interp_utility "BatSet.to_list (SymbolicUtility.dispatch' %1)" end module collection.Collection diff --git a/src/symbolic/symbolicInterpreter.mlw b/src/symbolic/symbolicInterpreter.mlw index 9c6f5082..06e228a3 100644 --- a/src/symbolic/symbolicInterpreter.mlw +++ b/src/symbolic/symbolicInterpreter.mlw @@ -35,14 +35,14 @@ module Semantics (* Instantiate the concrete semantics with the symbolic filesystem *) clone export semantics.Semantics with - type filesystem = filesystem, - axiom interp_utility_extends_output + type filesystem = filesystem end module SymState use list.List + use semantics.Context use Semantics (** A symbolic state combines a concrete context and a program state as defined in the @@ -67,6 +67,7 @@ module Results use list.ListRich as L use semantics.Input use semantics.Behaviour + use semantics.Context use Semantics use SymState @@ -303,28 +304,30 @@ module Interpreter use list.ListRich as L use set.Fset use string.OCaml as String - use syntax.Syntax - use auxiliaries.OptionGet + use collection.Collection as Cn - use semantics.Result + use auxiliaries.OptionGet + use syntax.Syntax + + use semantics.Arguments use semantics.Behaviour - use semantics.Input - use semantics.Config use semantics.Buffers - use semantics.Arguments + use semantics.Config + use semantics.Context use semantics.Env + use semantics.Input + use semantics.InterpUtilitySpec + use semantics.Path + use semantics.Result + use Constraints use import Semantics as S use import SymState as St use Results as Rs - (* Implemented in OCaml *) - val function sym_interp_utility (ctx:utility_context) (id:identifier) (sta:state) : Cn.t (state, result bool) - ensures { - forall res. - interp_utility ctx id sta = res <-> - Cn.mem res result - } + (* TODO remove `function` and use S.interp_utility in specs below *) + val function sym_interp_utility (params: (utility_context, identifier, state)) : Cn.t (state, result bool) + ensures { forall res. S.interp_utility params res <-> Cn.mem res result } let rec separate_results_list (sta_opts: list ('a, result 'b)) : (results: list ('a, 'b), failures: list 'a) variant { sta_opts } @@ -559,14 +562,15 @@ module Interpreter let res1, res1_failures = separate_results (interp_str_expr stk True inp ctx sta se) in - let function aux arg = - let sta1, (s, (_:bool)) = arg in - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let pwd_s = normalized_path_to_string cwd_p in - let args = Cons "-d" (Cons pwd_s Nil) in - Cn.map (fun arg -> let sta2, r = arg in ((sta2, cwd_p, pwd_s), r)) - (sym_interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1) in - let res2a = Cn.bind aux res1 in + let res2a = + let function aux arg = + let sta1, (s, (_:bool)) = arg in + let cwd_p = absolute_or_concat_relative ctx.cwd s in + let pwd_s = normalized_path_to_string cwd_p in + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons pwd_s Nil)) in + Cn.map (fun arg -> let sta2, r = arg in ((sta2, cwd_p, pwd_s), r)) + (sym_interp_utility (utility_ctx, identifier_test, sta1)) in + Cn.bind aux res1 in let res2, res2_failures = separate_results res2a in let function aux' arg = let (sta2, cwd_p, pwd_s), b = arg in @@ -596,10 +600,11 @@ module Interpreter let cwd_p = absolute_or_concat_relative ctx.cwd s in let pwd_s = normalized_path_to_string cwd_p in let args = Cons "-d" (Cons pwd_s Nil) in - (sta2, Incomplete) = interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1 -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Incomplete) -> Rs.mem {state=sta2; context=ctx; data=()} BIncomplete result by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Incomplete) (sym_interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1) + so Cn.mem (sta2, Incomplete) (sym_interp_utility (utility_ctx, identifier_test, sta1)) so Cn.mem (sta2, cwd_p, pwd_s) res2_failures } ensures { [@expl:cd_no_dir] @@ -608,12 +613,13 @@ module Interpreter let cwd_p = absolute_or_concat_relative ctx.cwd s in let pwd_s = normalized_path_to_string cwd_p in let args = Cons "-d" (Cons pwd_s Nil) in - (sta2, Ok False) = interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1 -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok False) -> let ctx' = {ctx with result = False} in let bhv = behaviour inp False in Rs.mem {state=sta2; context=ctx'; data=()} bhv result by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Ok False) (sym_interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1) + so Cn.mem (sta2, Ok False) (sym_interp_utility (utility_ctx, identifier_test, sta1)) so Cn.mem ((sta2, cwd_p, pwd_s), False) res2 so Cn.mem {state=sta2; context=ctx'; data=()} res2' } @@ -623,12 +629,13 @@ module Interpreter let cwd_p = absolute_or_concat_relative ctx.cwd s in let pwd_s = normalized_path_to_string cwd_p in let args = Cons "-d" (Cons pwd_s Nil) in - (sta2, Ok True) = interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1 -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in + interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok True) -> let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- pwd_s]'; cwd = cwd_p} in let bhv = behaviour inp True in Rs.mem {state=sta2; context=ctx1; data=()} bhv result by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Ok True) (sym_interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) identifier_test sta1) + so Cn.mem (sta2, Ok True) (sym_interp_utility (utility_ctx, identifier_test, sta1)) so Cn.mem ((sta2, cwd_p, pwd_s), True) res2 so Cn.mem {state=sta2; context=ctx1; data=()} res2' } @@ -639,7 +646,8 @@ module Interpreter (interp_list_expr stk inp ctx sta le) in let function aux arg = let sta', args = arg in - sym_interp_utility (ctx.cwd, filter_var_env ctx.var_env, args) id sta' in + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in + sym_interp_utility (utility_ctx, id, sta') in let res', res_failures' = separate_results (Cn.bind aux res) in let function aux' arg = @@ -657,14 +665,16 @@ module Interpreter ensures { [@expl:utility_incomplete] forall sta' ss sta''. eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - (sta'', Incomplete) = interp_utility (ctx.cwd, filter_var_env ctx.var_env, ss) id sta' -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in + interp_utility (utility_ctx, id, sta') (sta'', Incomplete) -> Rs.mem {state=sta''; context=ctx; data=()} BIncomplete result by Rs.mem {state=sta''; context=ctx; data=()} BIncomplete res_failures' } ensures { [@expl:utility] forall sta' ss sta'' b. eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - (sta'', Ok b) = interp_utility (ctx.cwd, filter_var_env ctx.var_env, ss) id sta' -> + let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in + interp_utility (utility_ctx, id, sta') (sta'', Ok b) -> let ctx' = {ctx with result=b} in Rs.mem {state=sta''; context=ctx'; data=()} (behaviour inp b) result by Cn.mem {state=sta''; context=ctx'; data=()} res'' diff --git a/src/symbolic/symbolicUtility.ml b/src/symbolic/symbolicUtility.ml index a419e265..2fbf85a4 100644 --- a/src/symbolic/symbolicUtility.ml +++ b/src/symbolic/symbolicUtility.ml @@ -258,7 +258,7 @@ let dispatch ~name = let call name ctx args = dispatch ~name {ctx with args} -let dispatch' (cwd, env, args) name sta = +let dispatch' ((cwd, env, args), name, sta) = let cwd = List.map Colis_constraints.Feat.from_string cwd in let ctx = {cwd; args; env} in BatSet.of_list (dispatch ~name ctx sta) diff --git a/src/symbolic/symbolicUtility.mli b/src/symbolic/symbolicUtility.mli index a65a76c4..20bb8637 100644 --- a/src/symbolic/symbolicUtility.mli +++ b/src/symbolic/symbolicUtility.mli @@ -147,4 +147,4 @@ val cmdliner_eval_utility : (**/**) (** A wrapper of [dispatch] for use in the Why3 driver *) -val dispatch' : (string list * string Env.IdMap.t * string list) -> string -> state -> (state * bool result) BatSet.t +val dispatch' : ((string list * string Env.IdMap.t * string list) * string * state) -> (state * bool result) BatSet.t From 5d1110291e3661010f85666ca9d881ec62060608 Mon Sep 17 00:00:00 2001 From: Benedikt Becker Date: Thu, 20 Feb 2020 02:31:50 +0100 Subject: [PATCH 2/4] Functorize symbolic engine and specialize for symbolic utilities --- src/colis.ml | 15 +- src/colis.mli | 23 +- src/concrete/driver.drv | 9 +- src/concrete/env.ml | 27 +- src/concrete/env.mli | 6 +- src/concrete/interpreter.mlw | 11 +- src/concrete/semantics.mlw | 82 +- src/concrete/utilities.ml | 8 +- src/symbolic/driver.drv | 11 - src/symbolic/symbolicInterpreter.mlw | 2509 ++++++++--------- src/symbolic/symbolicUtility.ml | 610 ++-- src/symbolic/symbolicUtility.mli | 254 +- src/symbolic/utilities/basics.ml | 3 +- .../utilities/colisInternalUnsafeTouch.ml | 2 +- src/symbolic/utilities/cp.ml | 3 +- src/symbolic/utilities/cp.mli | 3 +- src/symbolic/utilities/dpkg.ml | 2 +- .../utilities/dpkgMaintscriptHelper.ml | 8 +- .../utilities/dpkgMaintscriptHelper.mli | 3 +- src/symbolic/utilities/emacsPackage.ml | 2 +- src/symbolic/utilities/emacsPackage.mli | 5 +- src/symbolic/utilities/mkdir.ml | 2 +- src/symbolic/utilities/mkdir.mli | 3 +- src/symbolic/utilities/mv.ml | 2 +- src/symbolic/utilities/mv.mli | 3 +- src/symbolic/utilities/rm.ml | 2 +- src/symbolic/utilities/rm.mli | 4 +- src/symbolic/utilities/test.ml | 2 +- src/symbolic/utilities/test.mli | 6 +- src/symbolic/utilities/touch.ml | 2 +- src/symbolic/utilities/touch.mli | 4 +- src/symbolic/utilities/updateAlternatives.ml | 2 +- src/symbolic/utilities/updateMenus.ml | 2 +- src/symbolic/utilities/updateMenus.mli | 4 +- src/symbolic/utilities/which.ml | 2 +- src/symbolic/utilities/which.mli | 6 +- 36 files changed, 1826 insertions(+), 1816 deletions(-) diff --git a/src/colis.ml b/src/colis.ml index a056ee83..7005ff7e 100644 --- a/src/colis.ml +++ b/src/colis.ml @@ -33,16 +33,13 @@ module Concrete = struct end module Symbolic = struct - module Filesystem = SymbolicInterpreter__Filesystem + module Filesystem = SymbolicUtility.SymbolicFilesystem + include SymbolicUtility.Symbolic + module FilesystemSpec = FilesystemSpec - module Semantics = SymbolicInterpreter__Semantics - module SymState = SymbolicInterpreter__SymState - module Results = SymbolicInterpreter__Results - module Interpreter = SymbolicInterpreter__Interpreter - module Utility = SymbolicUtility let () = - List.iter SymbolicUtility.register [ + List.iter register [ (module Basics.True) ; (module Basics.Colon) ; (module Basics.False) ; @@ -81,7 +78,7 @@ module Symbolic = struct let var_env = add_var_bindings true vars empty_var_env in {empty_context with arguments; var_env; cwd=[]} in - {SymState.state; context; data=()} + {state; context} let interp_program ~loop_limit ~stack_size ~argument0 stas' program = let open Common in @@ -92,7 +89,7 @@ module Symbolic = struct let stack_size = Finite (Z.of_int stack_size) in { loop_limit; stack_size } in Input.({ argument0; config; under_condition=false }) in - let normals, errors, failures = Interpreter.interp_program inp stas' program in + let normals, errors, failures = interp_program inp stas' program in normals, errors, failures end diff --git a/src/colis.mli b/src/colis.mli index 0117a44e..ab53f77a 100644 --- a/src/colis.mli +++ b/src/colis.mli @@ -33,27 +33,24 @@ module Concrete : sig end module Symbolic : sig - module Filesystem = SymbolicInterpreter__Filesystem + module Filesystem = SymbolicUtility.SymbolicFilesystem + include module type of SymbolicUtility.Symbolic module FilesystemSpec = FilesystemSpec - module Semantics = SymbolicInterpreter__Semantics - module SymState = SymbolicInterpreter__SymState - module Results = SymbolicInterpreter__Results - module Interpreter = SymbolicInterpreter__Interpreter - module Utility = SymbolicUtility open Colis_constraints + open Semantics (** [compile_fs_spec root conj fs_spec] creates a disjunction that represents the conjunction [conj] with constraints representing the filesystem specified by [fs_spec] *) val add_fs_spec_to_clause : Var.t -> Clause.sat_conj -> FilesystemSpec.t -> Clause.sat_conj list (* Create a state corresponding to a conjunction *) - val to_state : prune_init_state:bool -> root:Var.t -> Clause.sat_conj -> Semantics.state + val to_state : prune_init_state:bool -> root:Var.t -> Clause.sat_conj -> state (* Create a symbolic states by adding context to a stringe *) - val to_symbolic_state : vars:(string * string) list -> arguments:string list -> Semantics.state -> unit SymState.sym_state + val to_symbolic_state : vars:(string * string) list -> arguments:string list -> state -> sym_state (* Wrapper around [Symbolic.Interpreter.interp_program] *) - val interp_program : loop_limit:int -> stack_size:int -> argument0:string -> unit SymState.sym_state list -> Language.Syntax.program -> (Semantics.state list * Semantics.state list * Semantics.state list) + val interp_program : loop_limit:int -> stack_size:int -> argument0:string -> sym_state list -> Language.Syntax.program -> (state list * state list * state list) end module Constraints = Colis_constraints @@ -124,13 +121,13 @@ type symbolic_config = { (** Maximum height of the call stack in symbolic execution *) } -open Symbolic +open Symbolic.Semantics -val print_symbolic_state : Format.formatter -> ?id:string -> Semantics.state -> unit +val print_symbolic_state : Format.formatter -> ?id:string -> state -> unit -val print_symbolic_states : initials:Semantics.state list -> (Semantics.state list * Semantics.state list * Semantics.state list) -> unit +val print_symbolic_states : initials:state list -> (state list * state list * state list) -> unit -val exit_code : (Semantics.state list * Semantics.state list * Semantics.state list) -> int +val exit_code : (state list * state list * state list) -> int val run_symbolic : symbolic_config -> Symbolic.FilesystemSpec.t -> argument0:string -> ?arguments:(string list) -> ?vars:((string * string) list) -> colis -> unit (** Symbolically executes a Colis program. *) diff --git a/src/concrete/driver.drv b/src/concrete/driver.drv index 955b23bd..51d8e573 100644 --- a/src/concrete/driver.drv +++ b/src/concrete/driver.drv @@ -3,6 +3,7 @@ module semantics.Env syntax val empty_env "Env.empty %1" syntax val ([]) "Env.get %1 %2" syntax val ([<-]) "Env.set %1 %2 %3" + syntax type env0 "%1 Env.SMap.t" end module semantics.Buffers @@ -11,9 +12,11 @@ module semantics.Buffers end module semantics.Path - syntax type feature "string" - syntax val normalized_path_to_string "String.concat \"/\" %1" - syntax val absolute_or_concat_relative "Utilities.absolute_or_concat_relative %1 %2" + syntax type feature "Colis_constraints.Feat.t" + syntax type normalized_path "Colis_constraints.Path.normal" + syntax val default_cwd "[]" + syntax val normalized_path_to_string "Colis_constraints.Path.normal_to_string %1" + syntax val absolute_or_concat_relative "Colis_constraints.Path.(normalize ~cwd:%1 (from_string %2))" end module semantics.Context diff --git a/src/concrete/env.ml b/src/concrete/env.ml index fc537cef..7f0cabbf 100644 --- a/src/concrete/env.ml +++ b/src/concrete/env.ml @@ -1,14 +1,19 @@ -module IdMap = Map.Make (String) -type 'a env = {map: 'a IdMap.t; default: 'a} -let empty default = {map=IdMap.empty; default} -let get env id = try IdMap.find id env.map with Not_found -> env.default -let set env id value = {env with map=IdMap.add id value env.map} -let filter p env = {env with map=IdMap.filter p env.map} -let map f default env = {map=IdMap.map f env.map; default} -let elements env = IdMap.fold (fun k v t -> (k, v) :: t) env.map [] +module SMap = Map.Make (String) + +type 'a env = {map: 'a SMap.t; default: 'a} + +let empty default = {map=SMap.empty; default} +let get env id = try SMap.find id env.map with Not_found -> env.default +let set env id value = {env with map=SMap.add id value env.map} +let filter p env = {env with map=SMap.filter p env.map} +let map f default env = {map=SMap.map f env.map; default} +let elements env = SMap.fold (fun k v t -> (k, v) :: t) env.map [] let to_map env = env.map -let filter_var_env (var_exported: 'a -> bool) (var_value: 'a -> string option) (env : 'a env) : string IdMap.t = +let filter_var_env (var_exported: 'a -> bool) (var_value: 'a -> 'b option) (env : 'a env) : 'b SMap.t = + let open SMap in env.map |> - IdMap.filter (fun _ v -> var_exported v && var_value v <> None) |> - IdMap.map (fun v -> match var_value v with Some s -> s | _ -> assert false) + filter (fun _ -> var_exported) |> + map var_value |> + filter (fun _ -> (<>) None) |> + map (function Some x -> x | _ -> assert false) diff --git a/src/concrete/env.mli b/src/concrete/env.mli index 442a585e..99d169d6 100644 --- a/src/concrete/env.mli +++ b/src/concrete/env.mli @@ -8,9 +8,9 @@ val filter : (string -> 'a -> bool) -> 'a env -> 'a env val map : ('a -> 'b) -> 'b -> 'a env -> 'b env val elements : 'a env -> (string * 'a) list -module IdMap : Map.S with type key = string +module SMap : Map.S with type key = string (** Conversion to a normal map without default value *) -val to_map : 'a env -> 'a IdMap.t +val to_map : 'a env -> 'a SMap.t -val filter_var_env : ('a -> bool) -> ('a -> string option) -> 'a env -> string IdMap.t +val filter_var_env : ('a -> bool) -> ('a -> 'b option) -> 'a env -> 'b SMap.t diff --git a/src/concrete/interpreter.mlw b/src/concrete/interpreter.mlw index 03565bd9..d2d518f2 100644 --- a/src/concrete/interpreter.mlw +++ b/src/concrete/interpreter.mlw @@ -74,7 +74,6 @@ module Interpreter use semantics.Input use semantics.Config use semantics.Context - use semantics.InterpUtilitySpec use semantics.Env as E use semantics.Path use Filesystem as Fs @@ -98,7 +97,7 @@ module Interpreter St.stdin = ref Stdin.empty; St.stdout = ref Stdout.empty; St.log = ref Stdout.empty; - St.cwd = ref Nil; + St.cwd = ref default_cwd; St.result = ref True; } @@ -152,23 +151,23 @@ module Interpreter let interp_utility' (inp:input) (sta:state) (id:identifier) (args:list string) ensures { - let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + let utility_ctx = {S.cwd= !(old sta.cwd); env=filter_var_env !(old sta.var_env); args=args} in interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Ok !(sta.result)) /\ behaviour inp !(sta.result) = BNormal } raises { EExit -> - let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + let utility_ctx = {S.cwd= !(old sta.cwd); env=filter_var_env !(old sta.var_env); args=args} in interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Ok !(sta.result)) /\ behaviour inp !(sta.result) = BExit } raises { EIncomplete -> - let utility_ctx = (!(old sta.cwd), filter_var_env !(old sta.var_env), args) in + let utility_ctx = {S.cwd= !(old sta.cwd); env=filter_var_env !(old sta.var_env); args=args} in interp_utility (utility_ctx, id, old sem_state sta) (sem_state sta, Incomplete) } raises { EIncomplete -> sem_context sta = sem_context (old sta) } - = let utility_ctx = (!(sta.cwd), filter_var_env !(sta.var_env), args) in + = let utility_ctx = {S.cwd= !(sta.cwd); env=filter_var_env !(sta.var_env); args=args} in let sta', r = interp_utility (utility_ctx, id, sem_state sta) in sta.filesystem := sta'.S.filesystem; assert { !(sta.filesystem) = sta'.S.filesystem }; diff --git a/src/concrete/semantics.mlw b/src/concrete/semantics.mlw index 0f1d3930..b3a03889 100644 --- a/src/concrete/semantics.mlw +++ b/src/concrete/semantics.mlw @@ -313,9 +313,9 @@ module Path "feature" in the tree constraint vocabulary *) type feature - type normalized_path = list feature + type normalized_path - let constant default_cwd : normalized_path = Nil + val constant default_cwd : normalized_path (** For `cd`: append `s` to `p` if `s` is relative or just `s` as path if it is absolute *) val function absolute_or_concat_relative (p:normalized_path) (s:string) : normalized_path @@ -399,28 +399,6 @@ module Context (* } *) end (* Context *) -(** Split the types to specify the interpretation of utilities to enable factorization - of the symbolic engine *) -module InterpUtilitySpec - use list.List - use syntax.Syntax - use Result - use Path - use Buffers - use Env - - type utility_context = (normalized_path (* cwd *), env0 string (* variable env *), list string (* args *)) - - type raw_state 'fs = ('fs, stdin, stdout, stdout (* log *)) - - predicate raw_interp_utility (utility_context, identifier, raw_state 'fs) (raw_state 'fs, result bool) - - axiom interp_utility_extends_output : forall ctx id stdin stdin' stdout stdout' log log' b, fs fs': 'fs. - raw_interp_utility (ctx, id, (fs, stdin, Stdout.empty, log)) ((fs', stdin', stdout', log'), b) -> - raw_interp_utility (ctx, id, (fs, stdin, stdout, log)) ((fs', stdin', Stdout.concat stdout' stdout', log), b) -end - - module Semantics use bool.Bool use int.Int @@ -436,13 +414,25 @@ module Semantics use Behaviour use Buffers use Config - use Context + use Context as Context use Env use Input - use InterpUtilitySpec use Path use Result + type utility_context = { + cwd: normalized_path; + env: env0 string; + args: list string + } + + let function utility_context args ctx = { + cwd = ctx.Context.cwd; + env = Context.filter_var_env ctx.Context.var_env; + args = args + } + use Context + (** {3 Evaluation state} The evaluation state combines standard input and stdout output with the filesystem. @@ -460,17 +450,11 @@ module Semantics log: stdout; (** Combines stdout with utility traces and error messages *) } - let function to_raw_state (s: state) : raw_state filesystem = - (s.filesystem, s.stdin, s.stdout, s.log) - - let function of_raw_state (s0: raw_state filesystem) : state = - let (filesystem, stdin, stdout, log) = s0 in - {filesystem = filesystem; stdin = stdin; stdout = stdout; log = log} + predicate interp_utility (utility_context, identifier, state) (state, result bool) - predicate interp_utility (params: (utility_context, identifier, state)) (res: (state, result bool)) = - let ctx, name, sta = params in - let sta', res = res in - raw_interp_utility (ctx, name, to_raw_state sta) (to_raw_state sta', res) + axiom interp_utility_extends_output : forall ctx id sta sta1 b. + interp_utility (ctx, id, {sta with stdout=Stdout.empty}) (sta1, b) -> + interp_utility (ctx, id, sta) ({sta with stdout=Stdout.concat sta.stdout sta1.stdout}, b) let function bool_of_return_code (c:return_code) (previous:bool) : bool = match c with @@ -558,17 +542,17 @@ module Semantics | eval_cd_incomplete : forall stk inp ctx sta se sta1 s _b sta2. eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Incomplete) -> + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let args = Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Incomplete) -> eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx, BIncomplete) | eval_cd_no_dir : forall stk inp ctx sta se sta1 s _b sta2. eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok False) -> + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let args = Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Ok False) -> let ctx1 = {ctx with result = False} in let bhv = behaviour inp False in eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx1, bhv) @@ -576,10 +560,10 @@ module Semantics | eval_cd : forall stk inp ctx sta se sta1 s _b sta2. eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, _b)) -> (* `test -d (CWD/)s` *) - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil)) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok True) -> - let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- normalized_path_to_string cwd_p]'; cwd = cwd_p} in + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let args = Cons "-d" (Cons (normalized_path_to_string cwd_p) Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Ok True) -> + let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- normalized_path_to_string cwd_p]'; Context.cwd = cwd_p} in let bhv = behaviour inp True in eval_instruction stk (inp, ctx, sta) (ICd se) (sta2, ctx1, bhv) @@ -670,14 +654,12 @@ module Semantics | eval_call_utility_incomplete: forall stk inp ctx sta sta' sta'' id es ss. eval_list_expr stk (inp, ctx, sta) es (sta', Ok ss) -> - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in - interp_utility (utility_ctx, id, sta') (sta'', Incomplete) -> + interp_utility (utility_context ss ctx, id, sta') (sta'', Incomplete) -> eval_instruction stk (inp, ctx, sta) (ICallUtility id es) (sta'', ctx, BIncomplete) | eval_call_utility: forall stk inp ctx sta sta' sta'' id es ss b. eval_list_expr stk (inp, ctx, sta) es (sta', Ok ss) -> - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in - interp_utility (utility_ctx, id, sta') (sta'', Ok b) -> + interp_utility (utility_context ss ctx, id, sta') (sta'', Ok b) -> let ctx' = {ctx with result=b} in let bhv = behaviour inp b in eval_instruction stk (inp, ctx, sta) (ICallUtility id es) (sta'', ctx', bhv) diff --git a/src/concrete/utilities.ml b/src/concrete/utilities.ml index f3d4f0e6..411c92e8 100644 --- a/src/concrete/utilities.ml +++ b/src/concrete/utilities.ml @@ -8,9 +8,7 @@ open Semantics__Result open Semantics__Buffers open Interpreter__Semantics -module IdMap = Env.IdMap - -type env = string IdMap.t +type env = string Env.SMap.t let incomplete ~utility msg = fun sta -> let str = utility ^ ": " ^ msg in @@ -42,7 +40,7 @@ let dpkg_compare_versions args = let dpkg_validate_thing subcmd arg = Sys.command ("dpkg " ^ subcmd ^ " " ^arg) = 0 -let interp_utility ((_cwd, var_env, args), id, sta) = +let interp_utility ({env; args; _}, id, sta) = match id with | "echo" -> let stdout = @@ -69,7 +67,7 @@ let interp_utility ((_cwd, var_env, args), id, sta) = let print_line sta line = {sta with stdout=Stdout.(sta.stdout |> output line |> newline)} in - IdMap.bindings var_env |> + Env.SMap.bindings env |> List.map format_line |> List.fold_left print_line sta, Ok true | _arg :: _ -> diff --git a/src/symbolic/driver.drv b/src/symbolic/driver.drv index b3b8aeee..819185c7 100644 --- a/src/symbolic/driver.drv +++ b/src/symbolic/driver.drv @@ -1,14 +1,3 @@ -module symbolicInterpreter.Constraints - syntax type clause "Colis_constraints.Clause.sat_conj" - syntax type variable "Colis_constraints.Var.t" - syntax type feature "Colis_constraints.Feat.t" - syntax type path "Colis_constraints.Path.t" -end - -module symbolicInterpreter.Interpreter - syntax val sym_interp_utility "BatSet.to_list (SymbolicUtility.dispatch' %1)" -end - module collection.Collection syntax type t "%1 list" syntax val mem "List.mem %1 %2" diff --git a/src/symbolic/symbolicInterpreter.mlw b/src/symbolic/symbolicInterpreter.mlw index 06e228a3..d33c174f 100644 --- a/src/symbolic/symbolicInterpreter.mlw +++ b/src/symbolic/symbolicInterpreter.mlw @@ -1,1334 +1,1277 @@ -module Constraints - - type variable - (** abstract type for variables denoting nodes in the file system *) - - type feature - (** abstract type for filenames (excluding "." and ".."), named - "feature" in the tree constraint vocabulary *) - - type clause - (** abstract type for tree constraints, [IMPORTANT] which are always satisfiable *) - - type path -end - -module Filesystem - use option.Option - use Constraints - - (** A symbolic filesystem composed by a variable indicating root, constraints, and the - current working directory `cwd` as feature path. root₀ may refer to the initial root - variable. When provided it should not be pruned (“quantified over”) during symbolic - execution of utilities. *) - type filesystem = { - root : variable; - clause : clause; - root0 : option variable; - } -end - -module Semantics - use list.List - use Filesystem - use Constraints - - (* Instantiate the concrete semantics with the symbolic filesystem *) - clone export semantics.Semantics with - type filesystem = filesystem -end - - -module SymState - - use list.List - use semantics.Context - use Semantics - - (** A symbolic state combines a concrete context and a program state as defined in the - semantics with a filesystem using feature constraints. *) - type sym_state 'a = { - context: context; - state: state; - data: 'a (* Used to fiddle additional information through interp_instr': A stdin in pipe and the last result in the loops *) - } - - let function with_data data = - fun sta -> - {sta with data = data} -end - -(** The symbolic interpretation of a program results describes multiple possible results - *) -module Results - - use set.Fset as Fset - use collection.Collection as Cn - use list.ListRich as L - use semantics.Input - use semantics.Behaviour - use semantics.Context - use Semantics - use SymState - - (** The results of a symbolic execution is a quadruple of sets of symbolic states, where - each components corresponds to a behaviour. *) - type t 'a = { - normal : Cn.t (sym_state 'a); - exit : Cn.t (sym_state 'a); - return_ : Cn.t (sym_state 'a); - failure : Cn.t (sym_state 'a); - } - - let constant empty = { - normal = Cn.empty; - exit = Cn.empty; - return_ = Cn.empty; - failure = Cn.empty; - } - - predicate mem (sta:sym_state 'a) (bhv:behaviour) (rs:t 'a) = - match bhv with - | BNormal -> Cn.mem sta rs.normal - | BExit -> Cn.mem sta rs.exit - | BReturn -> Cn.mem sta rs.return_ - | BIncomplete -> Cn.mem sta rs.failure - end - - let function map (f:sym_state 'a -> sym_state 'b) (res:t 'a) : t 'b - ensures { forall sta bhv. mem sta bhv res -> mem (f sta) bhv result } - ensures { forall sta' bhv. mem sta' bhv result -> exists sta. mem sta bhv res /\ sta' = f sta } - = { normal = Cn.map f res.normal; - exit = Cn.map f res.exit; - return_ = Cn.map f res.return_; - failure = Cn.map f res.failure; } - - let function union (res1 res2: t 'a) : t 'a - ensures { forall sta bhv. mem sta bhv res1 -> mem sta bhv result } - ensures { forall sta bhv. mem sta bhv res2 -> mem sta bhv result } - ensures { forall sta bhv. mem sta bhv result -> mem sta bhv res1 \/ mem sta bhv res2 } - = { normal = Cn.union res1.normal res2.normal; - exit = Cn.union res1.exit res2.exit; - return_ = Cn.union res1.return_ res2.return_; - failure = Cn.union res1.failure res2.failure; } - - lemma union_1: forall sta: sym_state 'a, bhv res1 res2. - mem sta bhv res1 -> mem sta bhv (union res1 res2) - - lemma union_2: forall sta: sym_state 'a, bhv res1 res2. - mem sta bhv res2 -> mem sta bhv (union res1 res2) - - let rec flatten_list (l: L.list (t 'a)) : t 'a - variant { l } - ensures { forall res sta bhv. L.mem res l -> mem sta bhv res -> mem sta bhv result } - ensures { forall sta bhv. mem sta bhv result -> exists res. L.mem res l /\ mem sta bhv res } - = match l with - | L.Nil -> empty - | L.Cons rs l' -> - union rs (flatten_list l') - end - - let flatten (c: Cn.t (t 'a)) : t 'a - ensures { forall res sta bhv. Cn.mem res c -> mem sta bhv res -> mem sta bhv result } - ensures { forall sta bhv. mem sta bhv result -> exists res. Cn.mem res c /\ mem sta bhv res } - = flatten_list (Cn.to_list c) - - let bind_collection (f: 'a -> t 'b) (c: Cn.t 'a) : t 'b - ensures { - forall x sta bhv. - Cn.mem x c -> - mem sta bhv (f x) -> - mem sta bhv result - } - ensures { - forall sta bhv. - mem sta bhv result -> - exists x. - Cn.mem x c /\ - mem sta bhv (f x) - } - = flatten (Cn.map f c) - - let separate_normal (res:t 'a) : (normal: Cn.t (sym_state 'a), other: t 'a) - ensures { normal = res.normal } - ensures { other.normal = Cn.empty } - ensures { other.exit = res.exit } - ensures { other.return_ = res.return_ } - ensures { other.failure = res.failure } - ensures { forall s. Cn.mem s normal -> Cn.mem s res.normal } - ensures { forall s bhv. mem s bhv other -> bhv <> BNormal /\ mem s bhv res } - = res.normal, {res with normal = Cn.empty} - - let function separate_non_failure (res:t 'a) : (Cn.t (sym_state 'a), Cn.t (sym_state 'a)) - = Cn.(union res.normal (union res.exit res.return_)), res.failure - - let function all_states (res:t 'a) : Cn.t (sym_state 'a) = - Cn.(union res.normal (union res.exit (union res.return_ res.failure))) - - let stas_as_failures (ctx:context) (stas:Cn.t state) : t unit - ensures { result.normal = Cn.empty } - ensures { result.exit = Cn.empty } - ensures { result.return_ = Cn.empty } - ensures { forall s. Cn.mem s result.failure -> Cn.mem s.state stas /\ s.context=ctx /\ s.data = () } - ensures { forall s. Cn.mem s stas -> Cn.mem {state=s; context=ctx; data=()} result.failure } - = let aux sta = {state=sta; context=ctx; data=()} in - {empty with failure=Cn.map aux stas} - - let function single_behaviour bhv (stas: Cn.t (sym_state 'a)) : t 'a = - match bhv with - | BNormal -> {empty with normal=stas} - | BExit -> {empty with exit=stas} - | BReturn -> {empty with return_=stas} - | BIncomplete -> {empty with failure=stas} - end - - let function inject (inp: input) (stas: Cn.t (sym_state 'a)) : t 'a - (* ensures { forall sta bhv. mem sta bhv result <-> Cn.mem sta stas /\ bhv = behaviour inp sta.context.result } *) - (* ensures { stas = Cn.union result.normal result.exit by Cn.(stas == union result.normal result.exit) } *) - (* ensures { forall s. Cn.mem s result.normal -> Cn.mem s stas /\ behaviour inp s.context.result = BNormal } *) - (* ensures { forall s. Cn.mem s result.exit -> Cn.mem s stas /\ behaviour inp s.context.result = BExit } *) - (* ensures { result.return_ = Cn.empty } *) - (* ensures { result.failure = Cn.empty } *) - = let exit, normal = Cn.partition (fun sta -> exit_not_normal inp sta.context.result) stas in - (* assert { forall sta. Cn.mem sta exit -> exit_not_normal inp sta.context.result = True }; *) - (* assert { forall sta. Cn.mem sta normal -> exit_not_normal inp sta.context.result = False }; *) - {empty with normal=normal; exit=exit} - (* ensures { forall sta bhv. mem sta bhv result -> Cn.mem sta normal \/ Cn.mem sta exit } *) - - lemma mem_inject: forall res: t 'a, sta bhv inp stas. - res = inject inp stas -> - (mem sta bhv res <-> Cn.mem sta stas /\ bhv = behaviour inp sta.context.result) - - lemma inject_union: forall res: t 'a, inp stas. - res = inject inp stas -> - stas = Cn.union res.normal res.exit by - Cn.(stas == union res.normal res.exit) - - lemma inject_return_empty: forall res: t 'a, inp stas. - res = inject inp stas -> res.return_ = Cn.empty - - lemma inject_failure_empty: forall res: t 'a, inp stas. - res = inject inp stas -> res.failure = Cn.empty - - lemma inject_normal_union_exit: forall inp, stas: Cn.t (sym_state 'a). - stas = Cn.union (inject inp stas).normal (inject inp stas).exit - by Cn.(stas == union (inject inp stas).normal (inject inp stas).exit) - - lemma inject_under_condition_normal: forall res: t 'a, inp stas. - res = inject inp stas -> - inp.under_condition = True -> - res.normal = stas - by Cn.(res.normal == stas) - - lemma inject_under_condition_exit: forall inp, stas: Cn.t (sym_state 'a). - inp.under_condition = True -> - (inject inp stas).exit = Cn.empty - by Cn.((inject inp stas).exit == Cn.empty) - - lemma inject_outside_condition_normal: forall inp, stas: Cn.t (sym_state 'a). - inp.under_condition = False -> - (inject inp stas).normal = Cn.filter (fun sta -> sta.context.result) stas - by Cn.((inject inp stas).normal == filter (fun sta -> sta.context.result) stas) - - lemma inject_outside_condition_exit: forall inp, stas: Cn.t (sym_state 'a). - inp.under_condition = False -> - (inject inp stas).exit = Cn.filter (fun sta -> not sta.context.result) stas - by Cn.((inject inp stas).exit == filter (fun sta -> not sta.context.result) stas) - - lemma inject_map: forall f: sym_state 'a -> sym_state 'b, res inp sta sta' bhv stas. - res = inject inp (Cn.map f stas) -> - sta' = f sta -> - bhv = behaviour inp (f sta).context.result -> - Cn.mem sta stas -> - mem sta' bhv res - - lemma inject_map': forall f: sym_state 'a -> sym_state 'b, res inp sta' bhv stas. - res = inject inp (Cn.map f stas) -> - mem sta' bhv res -> - bhv = behaviour inp sta'.context.result /\ - exists sta. Cn.mem sta stas /\ sta' = f sta - - lemma union_inject: forall res: t 'a, inp stas. - res = inject inp stas -> - stas = Cn.union res.normal res.exit - by - Cn.(stas == union res.normal res.exit) - - lemma mem_union_inject: forall sta: sym_state 'a, bhv inp stas rs. - mem sta bhv (union (inject inp stas) rs) <-> - ((Cn.mem sta stas /\ bhv = behaviour inp sta.context.result) \/ - mem sta bhv rs) - - lemma mem_union: forall sta bhv, rs1 rs2:t 'a. - mem sta bhv (union rs1 rs2) <-> - (mem sta bhv rs1 \/ mem sta bhv rs2) - - lemma mem_map: forall sta': sym_state 'b, bhv, rs, f: sym_state 'a -> sym_state 'b. - mem sta' bhv (map f rs) <-> - exists sta. mem sta bhv rs /\ sta' = f sta - - lemma failure_union: forall rs1 rs2: t 'a. - failure (union rs1 rs2) = Cn.union (failure rs1) (failure rs2) - - lemma mem_mk_results: forall sta: sym_state 'a, bhv normal exit return_ failure. - mem sta bhv { normal=normal; exit=exit; return_=return_; failure=failure } <-> - match bhv with - | BNormal -> Cn.mem sta normal - | BExit -> Cn.mem sta exit - | BReturn -> Cn.mem sta return_ - | BIncomplete -> Cn.mem sta failure - end - - lemma mem_union_results: forall sta: sym_state 'a, bhv rs1 rs2. - mem sta bhv (union rs1 rs2) <-> - (match bhv with - | BNormal -> Cn.mem sta rs1.normal - | BExit -> Cn.mem sta rs1.exit - | BReturn -> Cn.mem sta rs1.return_ - | BIncomplete -> Cn.mem sta rs1.failure - end \/ - match bhv with - | BNormal -> Cn.mem sta rs2.normal - | BExit -> Cn.mem sta rs2.exit - | BReturn -> Cn.mem sta rs2.return_ - | BIncomplete -> Cn.mem sta rs2.failure - end) -end - module Interpreter - - use bool.Bool - use option.Option - use int.Int - use list.List - use list.ListRich as L - use set.Fset - use string.OCaml as String - - use collection.Collection as Cn - use auxiliaries.OptionGet - use syntax.Syntax - - use semantics.Arguments - use semantics.Behaviour - use semantics.Buffers - use semantics.Config - use semantics.Context - use semantics.Env - use semantics.Input - use semantics.InterpUtilitySpec - use semantics.Path - use semantics.Result - - use Constraints - use import Semantics as S - use import SymState as St - use Results as Rs - - (* TODO remove `function` and use S.interp_utility in specs below *) - val function sym_interp_utility (params: (utility_context, identifier, state)) : Cn.t (state, result bool) - ensures { forall res. S.interp_utility params res <-> Cn.mem res result } - - let rec separate_results_list (sta_opts: list ('a, result 'b)) : (results: list ('a, 'b), failures: list 'a) - variant { sta_opts } - ensures { - forall sta x. - L.mem (sta, x) results <-> - L.mem (sta, Ok x) sta_opts - } - ensures { - forall sta. - L.mem sta failures <-> - L.mem (sta, Incomplete) sta_opts - } - = match sta_opts with - | Nil -> Nil, Nil - | Cons (sta, opt_x) sta_opts' -> - let results, failures = separate_results_list sta_opts' in - match opt_x with - | Ok x -> Cons (sta, x) results, failures - | Incomplete -> results, Cons sta failures - end - end - - let separate_results (sta_opts: Cn.t ('a, result 'b)) : (results: Cn.t ('a, 'b), failures: Cn.t 'a) - ensures { - forall sta x. - Cn.mem (sta, x) results <-> - Cn.mem (sta, Ok x) sta_opts - } - ensures { - forall sta. - Cn.mem sta failures <-> - Cn.mem (sta, Incomplete) sta_opts - } - = let results, failures = separate_results_list (Cn.to_list sta_opts) in - Cn.of_list results, Cn.of_list failures - - let rec function size_instr (ins:instruction) : int - variant { ins } - ensures { 0 < result } - = 1 + match ins with - | IReturn _ | IExit _ | IShift _ | IExport _ -> - 0 - | INot ins | INoOutput ins | ISubshell ins -> - size_instr ins - | ICd se | IAssignment _ se -> - size_string_expr se - | ICallUtility _ le | ICallFunction _ le -> - size_list_expr le - | ISequence ins1 ins2 | IPipe ins1 ins2 | IWhile ins1 ins2 -> - size_instr ins1 + size_instr ins2 - | IForeach _ le ins -> - size_list_expr le + size_instr ins - | IIf ins1 ins2 ins3 -> - size_instr ins1 + size_instr ins2 + size_instr ins3 - end - with function size_list_expr (le:list_expression): int - variant { le } - ensures { 0 < result } - = 1 + match le with - | Nil -> 0 - | Cons se_sp le' -> - size_pair se_sp + size_list_expr le' - end - with function size_pair (se_sp: (string_expression, split)) : int - variant { se_sp } - ensures { 0 < result } - = let se, _ = se_sp in - size_string_expr se - with function size_string_expr (se:string_expression): int - variant { se } - ensures { 0 < result } - = 1 + match se with - | SLiteral _ - | SVariable _ - | SArgument _ -> 0 - | SSubshell ins -> size_instr ins - | SConcat se1 se2 -> size_string_expr se1 + size_string_expr se2 + scope MakeSemantics + use bool.Bool + use int.Int + use list.List + use list.ListRich as L + use option.Option + use set.Fset + use string.String + + use syntax.Identifier + use syntax.Syntax + use auxiliaries.OptionGet + use collection.Collection as Cn + use semantics.Arguments + use semantics.Behaviour + use semantics.Buffers + use semantics.Config + use semantics.Context + use semantics.Input + use semantics.Path + use semantics.Result + + scope import Filesystem + type filesystem end - let rec lemma size_string_expr_list_expr (le:list_expression) - ensures { forall se sp. size_string_expr se < size_list_expr (Cons (se, sp) le) } - = match le with - | Nil -> () - | Cons _ le' -> size_string_expr_list_expr le' - end + clone export semantics.Semantics with + type filesystem = filesystem - let function flip_result sta - ensures { result.state = sta.state } - ensures { result.data = sta.data } - ensures { result.context = {sta.context with result=notb sta.context.result} } - = let result = notb sta.context.result in - let context = {sta.context with result=result} in - {sta with context=context} - - let function reset_output sta sta' = - let sta'' = {sta'.state with stdout = sta.stdout} in - {sta' with state = sta''} - - let function assignment_for_str_sta ctx var arg = - let sta', (str, result) = arg in - let ctx' = { - ctx with - var_env = ctx.var_env[var <- str]'; - result = result; - } - in - {state = sta'; context = ctx'; data = ()} - - let rec interp_instr (stk:int) (inp:input) (ctx:context) (sta:state) (ins:instruction) - : Rs.t unit - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { within_limit stk inp.config.stack_size } - variant { get_finite inp.config.stack_size - stk, size_instr ins, -1 } - ensures { (* Over-approximation *) - forall sta' ctx' bhv. - eval_instruction stk (inp, ctx, sta) ins (sta', ctx', bhv) -> - Rs.mem {state=sta'; context=ctx'; data=()} bhv result - } - (* (\* Under-approximation (->) and over-approximation (<-) *\) *) - (* ensures { *) - (* forall bhv sta'. *) - (* Rs.mem sta' bhv result <-> *) - (* eval_instruction stk (inp, ctx, sta) ins *) - (* (sta'.state, sta'.context, bhv) *) - (* } *) - = match ins with - - | INot ins1 -> - let res = interp_instr stk {inp with under_condition=True} ctx sta ins1 in - Rs.({ - normal = Cn.map flip_result res.normal; - return_ = Cn.map flip_result res.return_; - exit = res.exit; - failure = res.failure; - }) - ensures { - forall sta' ctx' bhv. - eval_instruction stk ({inp with under_condition=True}, ctx, sta) ins1 (sta', ctx', bhv) -> - match bhv with - | BNormal | BReturn -> Rs.mem {state=sta'; context={ctx' with result=notb ctx'.result}; data=()} bhv result - | BExit | BIncomplete -> Rs.mem {state=sta'; context=ctx'; data=()} bhv result - end - } + scope MakeInterpreter - | IAssignment var se -> - let str_stas, str_stas_failure = - separate_results - (interp_str_expr stk True inp ctx sta se) - in - let str_stas_failure = Rs.stas_as_failures ctx str_stas_failure in - let res = - Rs.inject inp - (Cn.map (fun arg -> assignment_for_str_sta ctx var arg) str_stas) - in - Rs.(union res str_stas_failure) - ensures { - forall sta'. - eval_str_expr stk True (inp, ctx, sta) se (sta', Incomplete) -> - Cn.mem {state=sta'; context=ctx; data=()} result.Rs.failure - } + scope import Arg + (* TODO Remove `function` from this and the below reformulation, use predicate in specifications below *) + val function sym_interp_utility (params: (utility_context, identifier, state)) : Cn.t (state, result bool) + ensures { forall res. interp_utility params res <-> Cn.mem res result } + end - | IExport id -> - let sta' = - let ctx' = - let var_val = {ctx.var_env[id] with exported=True} in - let var_env' = ctx.var_env[id <- var_val] in - {ctx with var_env = var_env'; result = True} - in - {state = sta; context = ctx'; data=()} - in - Rs.({empty with normal = Cn.singleton sta'}) - - | ISequence ins1 ins2 -> - let res1_normal, res1_other = - Rs.separate_normal - (interp_instr stk inp ctx sta ins1) - in - let res2 = interp_instr' stk inp res1_normal ins2 in - Rs.union res2 res1_other - - | ISubshell ins -> - let stas, stas_failure = - Rs.separate_non_failure - (interp_instr stk inp ctx sta ins) - in - assert { - forall sta' ctx' bhv. - bhv <> BIncomplete -> - eval_instruction stk (inp, ctx, sta) ins (sta', ctx', bhv) -> - Cn.mem {state=sta'; context=ctx'; data=()} stas - }; - let stas' = - let aux sta = - let ctx' = {ctx with result = sta.context.result} in - {state=sta.state; context=ctx'; data=()} - in - Cn.map aux stas - in - let old_ctx_with_res sta = {sta with context={ctx with result=sta.context.result}} in - Rs.(union - (inject inp stas') - {empty with failure=Cn.map old_ctx_with_res stas_failure}) - ensures { - forall sta' ctx'. - eval_instruction stk (inp, ctx, sta) ins (sta', ctx', BIncomplete) -> - Rs.mem {state=sta'; context={ctx with result=ctx'.result}; data=()} BIncomplete result + (** A symbolic state combines a concrete context and a program state as defined in the + semantics with a filesystem using feature constraints. *) + type sym_state 'a = { + context: context; + state: state; + data: 'a (* Used to fiddle additional information through interp_instr': A stdin in pipe and the last result in the loops *) } - | INoOutput ins -> - Rs.map (reset_output sta) - (interp_instr stk inp ctx sta ins) - - | IIf ins1 ins2 ins3 -> - let res1_normal, res1_other = - Rs.separate_normal - (interp_instr stk {inp with under_condition=True} ctx sta ins1) - in - let res2 = - let res1_true, res1_false = - Cn.partition - (fun sta -> sta.context.result) - res1_normal - in - Rs.union - (interp_instr' stk inp res1_true ins2) - (interp_instr' stk inp res1_false ins3) - in - Rs.union res2 res1_other - - | ICd se -> - let res1, res1_failures = - separate_results - (interp_str_expr stk True inp ctx sta se) in - let res2a = - let function aux arg = - let sta1, (s, (_:bool)) = arg in - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let pwd_s = normalized_path_to_string cwd_p in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, Cons "-d" (Cons pwd_s Nil)) in - Cn.map (fun arg -> let sta2, r = arg in ((sta2, cwd_p, pwd_s), r)) - (sym_interp_utility (utility_ctx, identifier_test, sta1)) in - Cn.bind aux res1 in - let res2, res2_failures = separate_results res2a in - let function aux' arg = - let (sta2, cwd_p, pwd_s), b = arg in - if b then - let ctx' = { - ctx with - var_env = ctx.var_env[identifier_pwd <- pwd_s]'; - cwd = cwd_p; - result = True } in - {state = sta2; context = ctx'; data = ()} - else - let ctx' = {ctx with result = False} in - {state = sta2; context = ctx'; data = ()} in - let res2' = Cn.map aux' res2 in - let res3 = Rs.inject inp res2' in - let res2_failures' = Cn.map (fun arg -> let sta, _, _ = arg in sta) res2_failures in - let res_failures = Rs.stas_as_failures ctx (Cn.union res1_failures res2_failures') in - Rs.union res3 res_failures - ensures { [@expl:cd_arg_failure] - forall sta1. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Incomplete) -> - Rs.mem {state=sta1; context=ctx; data=()} BIncomplete result - } - ensures { [@expl:cd_incomplete] - forall sta1 s b sta2. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let pwd_s = normalized_path_to_string cwd_p in - let args = Cons "-d" (Cons pwd_s Nil) in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Incomplete) -> - Rs.mem {state=sta2; context=ctx; data=()} BIncomplete result - by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Incomplete) (sym_interp_utility (utility_ctx, identifier_test, sta1)) - so Cn.mem (sta2, cwd_p, pwd_s) res2_failures - } - ensures { [@expl:cd_no_dir] - forall sta1 s b sta2. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let pwd_s = normalized_path_to_string cwd_p in - let args = Cons "-d" (Cons pwd_s Nil) in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok False) -> - let ctx' = {ctx with result = False} in - let bhv = behaviour inp False in - Rs.mem {state=sta2; context=ctx'; data=()} bhv result - by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Ok False) (sym_interp_utility (utility_ctx, identifier_test, sta1)) - so Cn.mem ((sta2, cwd_p, pwd_s), False) res2 - so Cn.mem {state=sta2; context=ctx'; data=()} res2' - } - ensures { [@expl:cd] - forall sta1 s b sta2. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> - let cwd_p = absolute_or_concat_relative ctx.cwd s in - let pwd_s = normalized_path_to_string cwd_p in - let args = Cons "-d" (Cons pwd_s Nil) in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in - interp_utility (utility_ctx, identifier_test, sta1) (sta2, Ok True) -> - let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- pwd_s]'; cwd = cwd_p} in - let bhv = behaviour inp True in - Rs.mem {state=sta2; context=ctx1; data=()} bhv result - by Cn.mem (sta1, (s, b)) res1 - so Cn.mem (sta2, Ok True) (sym_interp_utility (utility_ctx, identifier_test, sta1)) - so Cn.mem ((sta2, cwd_p, pwd_s), True) res2 - so Cn.mem {state=sta2; context=ctx1; data=()} res2' - } + let function with_data data = + fun sta -> + {sta with data = data} + + (** The symbolic interpretation of a program results describes multiple possible results + *) + scope Results + + (** The results of a symbolic execution is a quadruple of sets of symbolic states, where + each components corresponds to a behaviour. *) + type t 'a = { + normal : Cn.t (sym_state 'a); + exit : Cn.t (sym_state 'a); + return_ : Cn.t (sym_state 'a); + failure : Cn.t (sym_state 'a); + } - | ICallUtility id le -> - let res, res_failures = - separate_results - (interp_list_expr stk inp ctx sta le) in - let function aux arg = - let sta', args = arg in - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, args) in - sym_interp_utility (utility_ctx, id, sta') in - let res', res_failures' = - separate_results (Cn.bind aux res) in - let function aux' arg = - let sta'', b = arg in - {state=sta''; context={ctx with result=b}; data=()} in - let res'' = Cn.map aux' res' in - let res''' = Rs.inject inp res'' in - let res_failures' = Rs.stas_as_failures ctx (Cn.union res_failures res_failures') in - Rs.union res''' res_failures' - ensures { [@expl:args_incomplete] - forall sta'. - eval_list_expr stk (inp, ctx, sta) le (sta', Incomplete) -> - Rs.mem {state=sta'; context=ctx; data=()} BIncomplete result - } - ensures { [@expl:utility_incomplete] - forall sta' ss sta''. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in - interp_utility (utility_ctx, id, sta') (sta'', Incomplete) -> - Rs.mem {state=sta''; context=ctx; data=()} BIncomplete result - by Rs.mem {state=sta''; context=ctx; data=()} BIncomplete res_failures' - } - ensures { [@expl:utility] - forall sta' ss sta'' b. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - let utility_ctx = (ctx.cwd, filter_var_env ctx.var_env, ss) in - interp_utility (utility_ctx, id, sta') (sta'', Ok b) -> - let ctx' = {ctx with result=b} in - Rs.mem {state=sta''; context=ctx'; data=()} (behaviour inp b) result - by Cn.mem {state=sta''; context=ctx'; data=()} res'' - } + let constant empty = { + normal = Cn.empty; + exit = Cn.empty; + return_ = Cn.empty; + failure = Cn.empty; + } - | ICallFunction id le -> - let arg_res, arg_res_failures = - separate_results - (interp_list_expr stk inp ctx sta le) - in - assert { [@expl:callfun1] - forall sta1 args. - eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> - Cn.mem (sta1, args) arg_res - }; - let res = - match ctx.func_env[id] with - | Some ins -> (* Function defined as `ins` *) - if stk = get_finite inp.config.stack_size then (* Stack overflow *) - let aux arg = - let sta', _ = arg in - let sta' = { sta' with log = Stdout.(newline (output stack_limit_message sta'.log)) } in - {state=sta'; context=ctx; data=()} - in - Rs.({empty with failure = Cn.map aux arg_res}) - ensures { [@expl:callfun2] - forall sta1 args ins. - eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> - ctx.func_env[id] = Some ins -> - inp.config.stack_size = Finite stk -> - let sta1' = { sta1 with log = Stdout.(newline (output stack_limit_message sta1.log)) } in - Rs.mem {state=sta1'; context=ctx; data=()} BIncomplete result - } - else (* Execute function *) - let res2 = - let function aux (arg: (state, list string)) = - let sta1, args = arg in - let inp1 = { inp with argument0 = identifier_to_string id } in - let ctx1 = { ctx with arguments = args } in - interp_instr (stk+1) inp1 ctx1 sta1 ins - in - Rs.bind_collection aux arg_res - ensures { [@expl:callfun3] - forall sta1 args ins sta2 ctx2 bhv2. - eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> - ctx.func_env[id] = Some ins -> - inp.config.stack_size <> Finite stk -> - let inp1 = { inp with argument0 = identifier_to_string id } in - let ctx1 = { ctx with arguments = args } in - eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> - Rs.mem {state=sta2; context=ctx2; data=()} bhv2 result - } - in - let res2' = - let function aux (sta2: sym_state unit) = - {state=sta2.state; context={sta2.context with arguments = ctx.arguments}; data=()} - in - Rs.map aux res2 - ensures { [@expl:callfun4] - forall sta1 args ins sta2 ctx2 bhv2. - eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> - ctx.func_env[id] = Some ins -> - inp.config.stack_size <> Finite stk -> - let inp1 = { inp with argument0 = identifier_to_string id } in - let ctx1 = { ctx with arguments = args } in - eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> - Rs.mem {state=sta2; context={ctx2 with arguments = ctx.arguments}; data=()} bhv2 result - } - in - Rs.({res2' with normal=Cn.union res2'.normal res2'.return_; return_=Cn.empty}) - ensures { [@expl:callfun5] - forall sta1 args ins sta2 ctx2 bhv2. - eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> - ctx.func_env[id] = Some ins -> - inp.config.stack_size <> Finite stk -> - let inp1 = {inp with argument0=identifier_to_string id} in - let ctx1 = {ctx with arguments=args} in - eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> - let bhv' = match bhv2 with BNormal | BReturn -> BNormal | BExit -> BExit | BIncomplete -> BIncomplete end in - let ctx' = { ctx2 with arguments = ctx.arguments } in - Rs.mem {state=sta2; context=ctx'; data=()} bhv' result - } - | None -> (* Undefined function *) - let arg_res' = - let function aux (arg: (state, list string)) = - let sta', _ = arg in - {state=sta'; context={ctx with result=False}; data=()} - in - Cn.map aux arg_res - ensures { - forall sta' ss. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - ctx.func_env[id] = None -> - Cn.mem {state=sta'; context={ctx with result=False}; data=()} result - by Cn.mem (sta', ss) arg_res - } - in - Rs.single_behaviour (behaviour inp False) arg_res' - ensures { - forall sta' ss. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - ctx.func_env[id] = None -> - Rs.mem {state=sta'; context={ctx with result=False}; data=()} (behaviour inp False) result - } - end - in - let res_failures = Rs.stas_as_failures ctx arg_res_failures in - Rs.union res res_failures - - | IShift bn -> - match shift_arguments (option_get (mk_nat 1) bn).nat ctx.arguments with - | Some args -> - let ctx' = {ctx with result = True; arguments = args} in - let sta' = {state = sta; context = ctx'; data=()} in - Rs.({empty with normal = Cn.singleton sta'}) - | None -> - let ctx' = {ctx with result = False} in - let sta' = {state = sta; context = ctx'; data=()} in - Rs.inject inp (Cn.singleton sta') + predicate mem (sta:sym_state 'a) (bhv:behaviour) (rs:t 'a) = + match bhv with + | BNormal -> Cn.mem sta rs.normal + | BExit -> Cn.mem sta rs.exit + | BReturn -> Cn.mem sta rs.return_ + | BIncomplete -> Cn.mem sta rs.failure + end + + let function map (f:sym_state 'a -> sym_state 'b) (res:t 'a) : t 'b + ensures { forall sta bhv. mem sta bhv res -> mem (f sta) bhv result } + ensures { forall sta' bhv. mem sta' bhv result -> exists sta. mem sta bhv res /\ sta' = f sta } + = { normal = Cn.map f res.normal; + exit = Cn.map f res.exit; + return_ = Cn.map f res.return_; + failure = Cn.map f res.failure; } + + let function union (res1 res2: t 'a) : t 'a + ensures { forall sta bhv. mem sta bhv res1 -> mem sta bhv result } + ensures { forall sta bhv. mem sta bhv res2 -> mem sta bhv result } + ensures { forall sta bhv. mem sta bhv result -> mem sta bhv res1 \/ mem sta bhv res2 } + = { normal = Cn.union res1.normal res2.normal; + exit = Cn.union res1.exit res2.exit; + return_ = Cn.union res1.return_ res2.return_; + failure = Cn.union res1.failure res2.failure; } + + lemma union_1: forall sta: sym_state 'a, bhv res1 res2. + mem sta bhv res1 -> mem sta bhv (union res1 res2) + + lemma union_2: forall sta: sym_state 'a, bhv res1 res2. + mem sta bhv res2 -> mem sta bhv (union res1 res2) + + let rec flatten_list (l: L.list (t 'a)) : t 'a + variant { l } + ensures { forall res sta bhv. L.mem res l -> mem sta bhv res -> mem sta bhv result } + ensures { forall sta bhv. mem sta bhv result -> exists res. L.mem res l /\ mem sta bhv res } + = match l with + | L.Nil -> empty + | L.Cons rs l' -> + union rs (flatten_list l') + end + + let flatten (c: Cn.t (t 'a)) : t 'a + ensures { forall res sta bhv. Cn.mem res c -> mem sta bhv res -> mem sta bhv result } + ensures { forall sta bhv. mem sta bhv result -> exists res. Cn.mem res c /\ mem sta bhv res } + = flatten_list (Cn.to_list c) + + let bind_collection (f: 'a -> t 'b) (c: Cn.t 'a) : t 'b + ensures { + forall x sta bhv. + Cn.mem x c -> + mem sta bhv (f x) -> + mem sta bhv result + } + ensures { + forall sta bhv. + mem sta bhv result -> + exists x. + Cn.mem x c /\ + mem sta bhv (f x) + } + = flatten (Cn.map f c) + + let separate_normal (res:t 'a) : (normal: Cn.t (sym_state 'a), other: t 'a) + ensures { normal = res.normal } + ensures { other.normal = Cn.empty } + ensures { other.exit = res.exit } + ensures { other.return_ = res.return_ } + ensures { other.failure = res.failure } + ensures { forall s. Cn.mem s normal -> Cn.mem s res.normal } + ensures { forall s bhv. mem s bhv other -> bhv <> BNormal /\ mem s bhv res } + = res.normal, {res with normal = Cn.empty} + + let function separate_non_failure (res:t 'a) : (Cn.t (sym_state 'a), Cn.t (sym_state 'a)) + = Cn.(union res.normal (union res.exit res.return_)), res.failure + + let function all_states (res:t 'a) : Cn.t (sym_state 'a) = + Cn.(union res.normal (union res.exit (union res.return_ res.failure))) + + let stas_as_failures (ctx:context) (stas:Cn.t state) : t unit + ensures { result.normal = Cn.empty } + ensures { result.exit = Cn.empty } + ensures { result.return_ = Cn.empty } + ensures { forall s. Cn.mem s result.failure -> Cn.mem s.state stas /\ s.context=ctx /\ s.data = () } + ensures { forall s. Cn.mem s stas -> Cn.mem {state=s; context=ctx; data=()} result.failure } + = let aux sta = {state=sta; context=ctx; data=()} in + {empty with failure=Cn.map aux stas} + + let function single_behaviour bhv (stas: Cn.t (sym_state 'a)) : t 'a = + match bhv with + | BNormal -> {empty with normal=stas} + | BExit -> {empty with exit=stas} + | BReturn -> {empty with return_=stas} + | BIncomplete -> {empty with failure=stas} + end + + let function inject (inp: input) (stas: Cn.t (sym_state 'a)) : t 'a + (* ensures { forall sta bhv. mem sta bhv result <-> Cn.mem sta stas /\ bhv = behaviour inp sta.context.result } *) + (* ensures { stas = Cn.union result.normal result.exit by Cn.(stas == union result.normal result.exit) } *) + (* ensures { forall s. Cn.mem s result.normal -> Cn.mem s stas /\ behaviour inp s.context.result = BNormal } *) + (* ensures { forall s. Cn.mem s result.exit -> Cn.mem s stas /\ behaviour inp s.context.result = BExit } *) + (* ensures { result.return_ = Cn.empty } *) + (* ensures { result.failure = Cn.empty } *) + = let exit, normal = Cn.partition (fun sta -> exit_not_normal inp sta.context.result) stas in + (* assert { forall sta. Cn.mem sta exit -> exit_not_normal inp sta.context.result = True }; *) + (* assert { forall sta. Cn.mem sta normal -> exit_not_normal inp sta.context.result = False }; *) + {empty with normal=normal; exit=exit} + (* ensures { forall sta bhv. mem sta bhv result -> Cn.mem sta normal \/ Cn.mem sta exit } *) + + lemma mem_inject: forall res: t 'a, sta bhv inp stas. + res = inject inp stas -> + (mem sta bhv res <-> Cn.mem sta stas /\ bhv = behaviour inp sta.context.result) + + lemma inject_union: forall res: t 'a, inp stas. + res = inject inp stas -> + stas = Cn.union res.normal res.exit by + Cn.(stas == union res.normal res.exit) + + lemma inject_return_empty: forall res: t 'a, inp stas. + res = inject inp stas -> res.return_ = Cn.empty + + lemma inject_failure_empty: forall res: t 'a, inp stas. + res = inject inp stas -> res.failure = Cn.empty + + lemma inject_normal_union_exit: forall inp, stas: Cn.t (sym_state 'a). + stas = Cn.union (inject inp stas).normal (inject inp stas).exit + by Cn.(stas == union (inject inp stas).normal (inject inp stas).exit) + + lemma inject_under_condition_normal: forall res: t 'a, inp stas. + res = inject inp stas -> + inp.under_condition = True -> + res.normal = stas + by Cn.(res.normal == stas) + + lemma inject_under_condition_exit: forall inp, stas: Cn.t (sym_state 'a). + inp.under_condition = True -> + (inject inp stas).exit = Cn.empty + by Cn.((inject inp stas).exit == Cn.empty) + + lemma inject_outside_condition_normal: forall inp, stas: Cn.t (sym_state 'a). + inp.under_condition = False -> + (inject inp stas).normal = Cn.filter (fun sta -> sta.context.result) stas + by Cn.((inject inp stas).normal == filter (fun sta -> sta.context.result) stas) + + lemma inject_outside_condition_exit: forall inp, stas: Cn.t (sym_state 'a). + inp.under_condition = False -> + (inject inp stas).exit = Cn.filter (fun sta -> not sta.context.result) stas + by Cn.((inject inp stas).exit == filter (fun sta -> not sta.context.result) stas) + + lemma inject_map: forall f: sym_state 'a -> sym_state 'b, res inp sta sta' bhv stas. + res = inject inp (Cn.map f stas) -> + sta' = f sta -> + bhv = behaviour inp (f sta).context.result -> + Cn.mem sta stas -> + mem sta' bhv res + + lemma inject_map': forall f: sym_state 'a -> sym_state 'b, res inp sta' bhv stas. + res = inject inp (Cn.map f stas) -> + mem sta' bhv res -> + bhv = behaviour inp sta'.context.result /\ + exists sta. Cn.mem sta stas /\ sta' = f sta + + lemma union_inject: forall res: t 'a, inp stas. + res = inject inp stas -> + stas = Cn.union res.normal res.exit + by + Cn.(stas == union res.normal res.exit) + + lemma mem_union_inject: forall sta: sym_state 'a, bhv inp stas rs. + mem sta bhv (union (inject inp stas) rs) <-> + ((Cn.mem sta stas /\ bhv = behaviour inp sta.context.result) \/ + mem sta bhv rs) + + lemma mem_union: forall sta bhv, rs1 rs2:t 'a. + mem sta bhv (union rs1 rs2) <-> + (mem sta bhv rs1 \/ mem sta bhv rs2) + + lemma mem_map: forall sta': sym_state 'b, bhv, rs, f: sym_state 'a -> sym_state 'b. + mem sta' bhv (map f rs) <-> + exists sta. mem sta bhv rs /\ sta' = f sta + + lemma failure_union: forall rs1 rs2: t 'a. + failure (union rs1 rs2) = Cn.union (failure rs1) (failure rs2) + + lemma mem_mk_results: forall sta: sym_state 'a, bhv normal exit return_ failure. + mem sta bhv { normal=normal; exit=exit; return_=return_; failure=failure } <-> + match bhv with + | BNormal -> Cn.mem sta normal + | BExit -> Cn.mem sta exit + | BReturn -> Cn.mem sta return_ + | BIncomplete -> Cn.mem sta failure + end + + lemma mem_union_results: forall sta: sym_state 'a, bhv rs1 rs2. + mem sta bhv (union rs1 rs2) <-> + (match bhv with + | BNormal -> Cn.mem sta rs1.normal + | BExit -> Cn.mem sta rs1.exit + | BReturn -> Cn.mem sta rs1.return_ + | BIncomplete -> Cn.mem sta rs1.failure + end \/ + match bhv with + | BNormal -> Cn.mem sta rs2.normal + | BExit -> Cn.mem sta rs2.exit + | BReturn -> Cn.mem sta rs2.return_ + | BIncomplete -> Cn.mem sta rs2.failure + end) end - | IForeach id le ins -> - let lst_res, lst_res_failures = - separate_results - (interp_list_expr stk inp ctx sta le) - in - assert { - forall sta' ss. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - Cn.mem (sta', ss) lst_res - }; - let res_loop_cont = - (* Execute foreach loop on a result from interp_list_expr *) - let function aux arg = - let sta', ss = arg in - interp_foreach True stk inp ctx sta' id ss ins - in - Rs.bind_collection aux lst_res + let rec separate_results_list (sta_opts: list ('a, result 'b)) : (results: list ('a, 'b), failures: list 'a) + variant { sta_opts } ensures { - forall sta' ss sta'' ctx' bhv b. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - eval_foreach stk True (inp, ctx, sta') id ss ins (sta'', ctx', bhv) b -> - Rs.mem {state=sta''; context=ctx'; data=b} bhv result + forall sta x. + L.mem (sta, x) results <-> + L.mem (sta, Ok x) sta_opts } - in - let res_loop_cont' = - let function aux sta'' = - let ctx'' = {sta''.context with result=sta''.data} in - with_data () {sta'' with context = ctx''} - in - Rs.map aux res_loop_cont ensures { - forall sta' ss sta'' ctx' bhv b. - eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> - eval_foreach stk True (inp, ctx, sta') id ss ins (sta'', ctx', bhv) b -> - - Rs.mem {state=sta''; context={ctx' with result=b}; data=()} bhv result + forall sta. + L.mem sta failures <-> + L.mem (sta, Incomplete) sta_opts } - in - let res_failure = - Rs.stas_as_failures ctx lst_res_failures - ensures { - forall sta'. - eval_list_expr stk (inp, ctx, sta) le (sta', Incomplete) -> - Rs.mem {state=sta'; context=ctx; data=()} BIncomplete result - } - in - Rs.(union res_loop_cont' res_failure) - - | IWhile ins1 ins2 -> - let res = interp_while True 0 stk inp ctx sta ins1 ins2 in - let function aux_normal (sta': sym_state (int, bool)) = - let _, b = sta'.data in - {state=sta'.state; context={sta'.context with result=b}; data=()} - in - let function aux_other (sta': sym_state (int, bool)) = - {sta' with data=()} - in - Rs.({normal=Cn.map aux_normal res.normal; - exit=Cn.map aux_other res.exit; - return_=Cn.map aux_other res.return_; - failure=Cn.map aux_other res.failure}) - ensures { - forall sta' ctx' bhv ctr b. - bhv <> BNormal -> - eval_while stk 0 True (inp, ctx, sta) ins1 ins2 (sta', ctx', bhv) ctr b -> - Rs.mem {state=sta'; context=ctx'; data=()} bhv result - } - ensures { - forall sta' ctx' ctr b. - eval_while stk 0 True (inp, ctx, sta) ins1 ins2 (sta', ctx', BNormal) ctr b -> - Rs.mem {state=sta'; context={ctx' with result=b}; data=()} BNormal result - } + = match sta_opts with + | Nil -> Nil, Nil + | Cons (sta, opt_x) sta_opts' -> + let results, failures = separate_results_list sta_opts' in + match opt_x with + | Ok x -> Cons (sta, x) results, failures + | Incomplete -> results, Cons sta failures + end + end - | IPipe ins1 ins2 -> - let stas1, stas1_failure = - Rs.separate_non_failure - (interp_instr stk inp ctx {sta with stdout=Stdout.empty} ins1) - in - assert { - forall sta1 ctx1 bhv1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - Cn.mem {state=sta1; context=ctx1; data=()} stas1 - }; - let stas1': Cn.t (sym_state stdin) = (* data is sta1.state.stdin *) - let function aux (sta1: sym_state unit) = - {state={sta1.state with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.state.stdout}; context=ctx; data=sta1.state.stdin} - in - Cn.map aux stas1 - ensures { - forall sta1 ctx1 bhv1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - Cn.mem - {state={sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}; context=ctx; data=sta1.stdin} - result - } - in - let res2: Rs.t stdin = (* data is sta1.state.stdin *) - interp_instr' stk inp stas1' ins2 + let separate_results (sta_opts: Cn.t ('a, result 'b)) : (results: Cn.t ('a, 'b), failures: Cn.t 'a) ensures { - forall sta1 ctx1 bhv1 sta2 ctx2 bhv2. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - eval_instruction stk (inp, ctx, {sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}) ins2 (sta2, ctx2, bhv2) -> - Rs.mem {state=sta2; context=ctx2; data=sta1.stdin} bhv2 result + forall sta x. + Cn.mem (sta, x) results <-> + Cn.mem (sta, Ok x) sta_opts } - in - let res2' = - let function aux sta2 = - {state={sta2.state with stdin=sta2.data}; - context={ctx with result=sta2.context.result}; - data=()} - in - Rs.map aux res2 - ensures { - forall sta1 ctx1 bhv1 sta2 ctx2 bhv2. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - eval_instruction stk (inp, ctx, {sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}) ins2 (sta2, ctx2, bhv2) -> - Rs.mem {state={sta2 with stdin=sta1.stdin}; context={ctx with result=ctx2.result}; data=()} bhv2 result - } - in - let sta1_failure' = - let function aux (sta1: sym_state unit) = - {state={sta1.state with stdout=sta.stdout}; context=ctx; data=()} - in - Cn.map aux stas1_failure ensures { - forall sta1 ctx1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, BIncomplete) -> - Cn.mem {state={sta1 with stdout=sta.stdout}; context=ctx; data=()} result + forall sta. + Cn.mem sta failures <-> + Cn.mem (sta, Incomplete) sta_opts } - in - Rs.(union res2' {empty with failure=sta1_failure'}) - - | IExit code -> - let r = - match code with - | RPrevious -> ctx.result - | RSuccess -> True - | RFailure -> False + = let results, failures = separate_results_list (Cn.to_list sta_opts) in + Cn.of_list results, Cn.of_list failures + + let rec function size_instr (ins:instruction) : int + variant { ins } + ensures { 0 < result } + = 1 + match ins with + | IReturn _ | IExit _ | IShift _ | IExport _ -> + 0 + | INot ins | INoOutput ins | ISubshell ins -> + size_instr ins + | ICd se | IAssignment _ se -> + size_string_expr se + | ICallUtility _ le | ICallFunction _ le -> + size_list_expr le + | ISequence ins1 ins2 | IPipe ins1 ins2 | IWhile ins1 ins2 -> + size_instr ins1 + size_instr ins2 + | IForeach _ le ins -> + size_list_expr le + size_instr ins + | IIf ins1 ins2 ins3 -> + size_instr ins1 + size_instr ins2 + size_instr ins3 end - in - let sta = {state=sta; context={ctx with result=r}; data=()} in - Rs.({empty with exit = Cn.singleton sta}) - - | IReturn code -> - let r = - match code with - | RPrevious -> ctx.result - | RSuccess -> True - | RFailure -> False + with function size_list_expr (le:list_expression): int + variant { le } + ensures { 0 < result } + = 1 + match le with + | Nil -> 0 + | Cons se_sp le' -> + size_pair se_sp + size_list_expr le' + end + with function size_pair (se_sp: (string_expression, split)) : int + variant { se_sp } + ensures { 0 < result } + = let se, _ = se_sp in + size_string_expr se + with function size_string_expr (se:string_expression): int + variant { se } + ensures { 0 < result } + = 1 + match se with + | SLiteral _ + | SVariable _ + | SArgument _ -> 0 + | SSubshell ins -> size_instr ins + | SConcat se1 se2 -> size_string_expr se1 + size_string_expr se2 end - in - let sta = - let ctx' = {ctx with result = r} in - {state = sta; context = ctx'; data=()} - in - Rs.({empty with return_ = Cn.singleton sta}) - end - with interp_foreach (b:bool) (stk:int) (inp:input) (ctx:context) (sta:state) (id:identifier) (ss:list string) (ins:instruction) : Rs.t bool - variant { get_finite inp.config.stack_size - stk, size_instr ins, L.length ss } - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { stk <= get_finite inp.config.stack_size } - ensures { - forall sta' ctx' bhv b'. - eval_foreach stk b (inp, ctx, sta) id ss ins (sta', ctx', bhv) b' -> - Rs.mem {state=sta'; context=ctx'; data=b'} bhv result - } - = match ss with - | Nil -> - Rs.({empty with normal=Cn.singleton {state=sta; context=ctx; data=b}}) - | Cons s ss' -> - let res1_normal, res1_other = - Rs.separate_normal - (interp_instr stk inp {ctx with var_env = ctx.var_env[id <- s]'} sta ins) - in - let res_normal' = - let function aux (sta1: sym_state unit) = - interp_foreach sta1.context.result stk inp sta1.context sta1.state id ss' ins - in - Rs.bind_collection aux res1_normal - ensures { - forall sta1 ctx1 sta2 ctx2 bhv2 b2. - eval_instruction stk (inp, {ctx with var_env=ctx.var_env[id <- s]'}, sta) ins (sta1, ctx1, BNormal) -> - eval_foreach stk ctx1.result (inp, ctx1, sta1) id ss' ins (sta2, ctx2, bhv2) b2 -> - Rs.mem {state=sta2; context=ctx2; data=b2} bhv2 result + let rec lemma size_string_expr_list_expr (le:list_expression) + ensures { forall se sp. size_string_expr se < size_list_expr (Cons (se, sp) le) } + = match le with + | Nil -> () + | Cons _ le' -> size_string_expr_list_expr le' + end + + let function flip_result sta + ensures { result.state = sta.state } + ensures { result.data = sta.data } + ensures { result.context = {sta.context with result=notb sta.context.result} } + = let result = notb sta.context.result in + let context = {sta.context with result=result} in + {sta with context=context} + + let function reset_output sta sta' = + let sta'' = {sta'.state with stdout = sta.stdout} in + {sta' with state = sta''} + + let function assignment_for_str_sta ctx var arg = + let sta', (str, result) = arg in + let ctx' = { + ctx with + var_env = ctx.var_env[var <- str]'; + result = result; } - in - let res1_other' = - let function aux (sta: sym_state unit) = - with_data sta.context.result sta in - Rs.map aux res1_other - ensures { - forall sta1 ctx1 bhv1. - eval_instruction stk (inp, {ctx with var_env=ctx.var_env[id <- s]'}, sta) ins (sta1, ctx1, bhv1) -> - bhv1 <> BNormal -> - Rs.mem {state=sta1; context=ctx1; data=ctx1.result} bhv1 result + {state = sta'; context = ctx'; data = ()} + + let rec interp_instr (stk:int) (inp:input) (ctx:context) (sta:state) (ins:instruction) + : Results.t unit + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { within_limit stk inp.config.stack_size } + variant { get_finite inp.config.stack_size - stk, size_instr ins, -1 } + ensures { (* Over-approximation *) + forall sta' ctx' bhv. + eval_instruction stk (inp, ctx, sta) ins (sta', ctx', bhv) -> + Results.mem {state=sta'; context=ctx'; data=()} bhv result } - in - Rs.union res_normal' res1_other' - end + (* (\* Under-approximation (->) and over-approximation (<-) *\) *) + (* ensures { *) + (* forall bhv sta'. *) + (* Results.mem sta' bhv result <-> *) + (* eval_instruction stk (inp, ctx, sta) ins *) + (* (sta'.state, sta'.context, bhv) *) + (* } *) + = match ins with + + | INot ins1 -> + let res = interp_instr stk {inp with under_condition=True} ctx sta ins1 in + Results.({ + normal = Cn.map flip_result res.normal; + return_ = Cn.map flip_result res.return_; + exit = res.exit; + failure = res.failure; + }) + ensures { + forall sta' ctx' bhv. + eval_instruction stk ({inp with under_condition=True}, ctx, sta) ins1 (sta', ctx', bhv) -> + match bhv with + | BNormal | BReturn -> Results.mem {state=sta'; context={ctx' with result=notb ctx'.result}; data=()} bhv result + | BExit | BIncomplete -> Results.mem {state=sta'; context=ctx'; data=()} bhv result + end + } - with interp_while b ctr stk inp ctx (sta:state) ins1 ins2 : Rs.t (int, bool) - variant { get_finite inp.config.stack_size - stk, size_instr ins1+size_instr ins2, get_finite inp.config.loop_limit - ctr } - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { 0 <= ctr <= get_finite inp.config.loop_limit } - requires { stk <= get_finite inp.config.stack_size } - ensures { - forall sta' ctx' bhv ctr' b'. - eval_while stk ctr b (inp, ctx, sta) ins1 ins2 (sta', ctx', bhv) ctr' b' -> - Rs.mem {state=sta'; context=ctx'; data=(ctr', b')} bhv result - } - = let loop_limit = get_finite inp.config.loop_limit in - if ctr = loop_limit then - let sta = { sta with log = Stdout.(newline (output loop_limit_message sta.log)) } in - Rs.({empty with failure=Cn.singleton {state=sta; context=ctx; data=(ctr, b)}}) - else - let res1_normal, res1_abort = - Rs.separate_normal - (interp_instr stk {inp with under_condition=True} ctx sta ins1) - in - assert { - forall sta1 ctx1. - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - Cn.mem {state=sta1; context=ctx1; data=()} res1_normal - }; - assert { - forall sta1 ctx1 bhv1. - bhv1 <> BNormal -> - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, bhv1) -> - Rs.mem {state=sta1; context=ctx1; data=()} bhv1 res1_abort - }; - let res1_true, res1_false = - Cn.partition (fun sta -> sta.context.result) res1_normal - in - assert { - forall sta1 ctx1. - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = True -> - Cn.mem {state=sta1; context=ctx1; data=()} res1_true - }; - assert { - forall sta1 ctx1. - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = False -> - Cn.mem {state=sta1; context=ctx1; data=()} res1_false - }; - let res2_normal, res2_abort = - Rs.separate_normal - (interp_instr' stk inp res1_true ins2) - in - assert { - forall sta1 ctx1 sta2 ctx2. - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = True -> - eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, BNormal) -> - Cn.mem {state=sta2; context=ctx2; data=()} res2_normal - }; - assert { - forall sta1 ctx1 sta2 ctx2 bhv2. - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = True -> - eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, bhv2) -> - bhv2 <> BNormal -> - Rs.mem {state=sta2; context=ctx2; data=()} bhv2 res2_abort - }; - let res3 = - let function aux (sta2:sym_state unit) = - interp_while sta2.context.result (ctr+1) stk inp sta2.context sta2.state ins1 ins2 - in - Rs.bind_collection aux res2_normal - ensures { - forall sta1 ctx1 sta2 ctx2 sta3 ctx3 bhv3 n b'. - inp.config.loop_limit <> Finite ctr -> - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = True -> - eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, BNormal) -> - eval_while stk (ctr+1) ctx2.result (inp, ctx2, sta2) ins1 ins2 (sta3, ctx3, bhv3) n b' -> - Rs.mem {state=sta3;context=ctx3;data=(n, b')} bhv3 result - } - in - let function with_ctr_b (sta:sym_state unit) = - {sta with data=(ctr, b)} - in - let res1_abort' = - Rs.map with_ctr_b res1_abort - ensures { - forall sta1 ctx1 bhv1. - inp.config.loop_limit <> Finite ctr -> - bhv1 <> BNormal -> - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, bhv1) -> - Rs.mem {state=sta1;context=ctx1;data=(ctr, b)} bhv1 result - } - in - let res1_false' = - Rs.({empty with normal=Cn.map with_ctr_b res1_false}) - ensures { - forall sta1 ctx1. - inp.config.loop_limit <> Finite ctr -> - ctx1.result = False -> - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - Rs.mem {state=sta1;context=ctx1;data=(ctr, b)} BNormal result - } - in - let res2_abort' = - Rs.map with_ctr_b res2_abort - ensures { - forall sta1 ctx1 sta2 ctx2 bhv2. - inp.config.loop_limit <> Finite ctr -> - eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> - ctx1.result = True -> - eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, bhv2) -> - bhv2 <> BNormal -> - Rs.mem {state=sta2;context=ctx2;data=(ctr, b)} bhv2 result - } - in - Rs.(union res3 (union res1_abort' (union res1_false' res2_abort'))) - - with interp_instr' (stk:int) (inp:input) (stas: Cn.t (sym_state 'a)) (ins:instruction) : Rs.t 'a - variant { get_finite inp.config.stack_size - stk, size_instr ins, Cn.size stas } - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { stk <= get_finite inp.config.stack_size } - ensures { (* Over-approximation *) - forall sta sta' ctx' bhv. - Cn.mem sta stas -> - eval_instruction stk (inp, sta.context, sta.state) ins (sta', ctx', bhv) -> - Rs.mem {state=sta'; context=ctx'; data=sta.data} bhv result - } - = let interp_instr_with_data sta = - ensures { - forall sta' ctx' bhv. - eval_instruction stk (inp, sta.context, sta.state) ins (sta', ctx', bhv) -> - Rs.mem {state=sta'; context=ctx'; data=sta.data} bhv result - } - Rs.map (with_data sta.data) - (interp_instr stk inp sta.context sta.state ins) - in - Rs.bind_collection interp_instr_with_data stas - - with interp_str_expr (stk:int) (b:bool) (inp:input) (ctx:context) (sta:state) (se:string_expression) - : Cn.t (state, result (string, bool)) - variant { get_finite inp.config.stack_size - stk, size_string_expr se, -1 } - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { stk <= get_finite inp.config.stack_size } - ensures { (* Over-approximation *) - forall sta' res. - eval_str_expr stk b (inp, ctx, sta) se (sta', res) -> - Cn.mem (sta', res) result - } - = match se with - | SLiteral str -> - Cn.singleton (sta, Ok (str, b)) - | SVariable var -> - let str = ctx.var_env[var]' in - Cn.singleton (sta, Ok (str, b)) - | SArgument n -> - let str = nth_argument (Cons inp.argument0 ctx.arguments) n.nat in - Cn.singleton (sta, Ok (str, b)) - | SSubshell ins -> - let res, stas_failure = - Rs.separate_non_failure - (interp_instr stk inp ctx {sta with stdout=Stdout.empty} ins) - in - assert { - forall sta1 ctx1 bhv1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - Cn.mem {state=sta1; context=ctx1; data=()} res - }; - assert { - forall sta1 ctx1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, BIncomplete) -> - Cn.mem {state=sta1; context=ctx1; data=()} stas_failure - }; - let res' = - let for_res (sta1:sym_state unit) = - let str = Stdout.to_string sta1.state.stdout in - let sta1' = {sta1.state with stdout=sta.stdout} in - let b' = sta1.context.result in - sta1', Ok (str, b') - in - Cn.map for_res res - in - let res_failure' = - Cn.map (fun sta1 -> {sta1.state with stdout=sta.stdout}, Incomplete) stas_failure - in - Cn.union res' res_failure' - ensures { - forall sta1 ctx1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, BIncomplete) -> - Cn.mem ({sta1 with stdout = sta.stdout}, Incomplete) result - by Cn.mem ({sta1 with stdout = sta.stdout}, Incomplete) res_failure' - } - ensures { - forall sta1 ctx1 bhv1. - eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, bhv1) -> - bhv1 <> BIncomplete -> - Cn.mem ({sta1 with stdout = sta.stdout}, Ok (Stdout.to_string sta1.stdout, ctx1.result)) result - by Cn.mem ({sta1 with stdout = sta.stdout}, Ok (Stdout.to_string sta1.stdout, ctx1.result)) res' - } - | SConcat se1 se2 -> - let res1, res1_failure = - separate_results - (interp_str_expr stk b inp ctx sta se1) - in - let res1' = - let for_res1 arg + | IAssignment var se -> + let str_stas, str_stas_failure = + separate_results + (interp_str_expr stk True inp ctx sta se) + in + let str_stas_failure = Results.stas_as_failures ctx str_stas_failure in + let res = + Results.inject inp + (Cn.map (fun arg -> assignment_for_str_sta ctx var arg) str_stas) + in + Results.(union res str_stas_failure) ensures { - let sta1, (str1, b1) = arg in - forall sta2. - eval_str_expr stk b (inp, ctx, sta) se1 (sta1, Ok (str1, b1)) -> - eval_str_expr stk b1 (inp, ctx, sta1) se2 (sta2, Incomplete) -> - Cn.mem (sta2, Incomplete) result + forall sta'. + eval_str_expr stk True (inp, ctx, sta) se (sta', Incomplete) -> + Cn.mem {state=sta'; context=ctx; data=()} result.Results.failure } + + | IExport id -> + let sta' = + let ctx' = + let var_val = {ctx.var_env[id] with exported=True} in + let var_env' = ctx.var_env[id <- var_val] in + {ctx with var_env = var_env'; result = True} + in + {state = sta; context = ctx'; data=()} + in + Results.({empty with normal = Cn.singleton sta'}) + + | ISequence ins1 ins2 -> + let res1_normal, res1_other = + Results.separate_normal + (interp_instr stk inp ctx sta ins1) + in + let res2 = interp_instr' stk inp res1_normal ins2 in + Results.union res2 res1_other + + | ISubshell ins -> + let stas, stas_failure = + Results.separate_non_failure + (interp_instr stk inp ctx sta ins) + in + assert { + forall sta' ctx' bhv. + bhv <> BIncomplete -> + eval_instruction stk (inp, ctx, sta) ins (sta', ctx', bhv) -> + Cn.mem {state=sta'; context=ctx'; data=()} stas + }; + let stas' = + let aux sta = + let ctx' = {ctx with result = sta.context.result} in + {state=sta.state; context=ctx'; data=()} + in + Cn.map aux stas + in + let old_ctx_with_res sta = {sta with context={ctx with result=sta.context.result}} in + Results.(union + (inject inp stas') + {empty with failure=Cn.map old_ctx_with_res stas_failure}) ensures { - let sta1, (str1, b1) = arg in - forall sta2 str2 b2. - eval_str_expr stk b (inp, ctx, sta) se1 (sta1, Ok (str1, b1)) -> - eval_str_expr stk b1 (inp, ctx, sta1) se2 (sta2, Ok (str2, b2)) -> - Cn.mem (sta2, Ok (String.concat str1 str2, b2)) result + forall sta' ctx'. + eval_instruction stk (inp, ctx, sta) ins (sta', ctx', BIncomplete) -> + Results.mem {state=sta'; context={ctx with result=ctx'.result}; data=()} BIncomplete result + } + + | INoOutput ins -> + Results.map (reset_output sta) + (interp_instr stk inp ctx sta ins) + + | IIf ins1 ins2 ins3 -> + let res1_normal, res1_other = + Results.separate_normal + (interp_instr stk {inp with under_condition=True} ctx sta ins1) + in + let res2 = + let res1_true, res1_false = + Cn.partition + (fun sta -> sta.context.result) + res1_normal + in + Results.union + (interp_instr' stk inp res1_true ins2) + (interp_instr' stk inp res1_false ins3) + in + Results.union res2 res1_other + + | ICd se -> + let res1, res1_failures = + separate_results + (interp_str_expr stk True inp ctx sta se) in + let res2a = + let function aux arg = + let sta1, (s, (_:bool)) = arg in + let cwd_p = absolute_or_concat_relative ctx.cwd s in + let pwd_s = normalized_path_to_string cwd_p in + let args = Cons "-d" (Cons pwd_s Nil) in + Cn.map (fun arg -> let sta2, r = arg in ((sta2, cwd_p, pwd_s), r)) + (sym_interp_utility (utility_context args ctx, identifier_test, sta1)) in + Cn.bind aux res1 in + let res2, res2_failures = separate_results res2a in + let function aux' arg = + let (sta2, cwd_p, pwd_s), b = arg in + if b then + let ctx' = { + ctx with + var_env = ctx.var_env[identifier_pwd <- pwd_s]'; + Context.cwd = cwd_p; + result = True } in + {state = sta2; context = ctx'; data = ()} + else + let ctx' = {ctx with result = False} in + {state = sta2; context = ctx'; data = ()} in + let res2' = Cn.map aux' res2 in + let res3 = Results.inject inp res2' in + let res2_failures' = Cn.map (fun arg -> let sta, _, _ = arg in sta) res2_failures in + let res_failures = Results.stas_as_failures ctx (Cn.union res1_failures res2_failures') in + Results.union res3 res_failures + ensures { [@expl:cd_arg_failure] + forall sta1. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Incomplete) -> + Results.mem {state=sta1; context=ctx; data=()} BIncomplete result + } + ensures { [@expl:cd_incomplete] + forall sta1 s b sta2. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let pwd_s = normalized_path_to_string cwd_p in + let args = Cons "-d" (Cons pwd_s Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Incomplete) -> + Results.mem {state=sta2; context=ctx; data=()} BIncomplete result + by Cn.mem (sta1, (s, b)) res1 + so Cn.mem (sta2, Incomplete) (sym_interp_utility (utility_context args ctx, identifier_test, sta1)) + so Cn.mem (sta2, cwd_p, pwd_s) res2_failures + } + ensures { [@expl:cd_no_dir] + forall sta1 s b sta2. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let pwd_s = normalized_path_to_string cwd_p in + let args = Cons "-d" (Cons pwd_s Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Ok False) -> + let ctx' = {ctx with result = False} in + let bhv = behaviour inp False in + Results.mem {state=sta2; context=ctx'; data=()} bhv result + by Cn.mem (sta1, (s, b)) res1 + so Cn.mem (sta2, Ok False) (sym_interp_utility (utility_context args ctx, identifier_test, sta1)) + so Cn.mem ((sta2, cwd_p, pwd_s), False) res2 + so Cn.mem {state=sta2; context=ctx'; data=()} res2' } - = let sta1, (str1, b1) = arg in - let res2, res2_failure = + ensures { [@expl:cd] + forall sta1 s b sta2. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (s, b)) -> + let cwd_p = absolute_or_concat_relative ctx.Context.cwd s in + let pwd_s = normalized_path_to_string cwd_p in + let args = Cons "-d" (Cons pwd_s Nil) in + interp_utility (utility_context args ctx, identifier_test, sta1) (sta2, Ok True) -> + let ctx1 = {ctx with result = True; var_env = ctx.var_env[identifier_pwd <- pwd_s]'; Context.cwd = cwd_p} in + let bhv = behaviour inp True in + Results.mem {state=sta2; context=ctx1; data=()} bhv result + by Cn.mem (sta1, (s, b)) res1 + so Cn.mem (sta2, Ok True) (sym_interp_utility (utility_context args ctx, identifier_test, sta1)) + so Cn.mem ((sta2, cwd_p, pwd_s), True) res2 + so Cn.mem {state=sta2; context=ctx1; data=()} res2' + } + + | ICallUtility id le -> + let res, res_failures = + separate_results + (interp_list_expr stk inp ctx sta le) in + let function aux arg = + let sta', args = arg in + sym_interp_utility (utility_context args ctx, id, sta') in + let res', res_failures' = + separate_results (Cn.bind aux res) in + let function aux' arg = + let sta'', b = arg in + {state=sta''; context={ctx with result=b}; data=()} in + let res'' = Cn.map aux' res' in + let res''' = Results.inject inp res'' in + let res_failures' = Results.stas_as_failures ctx (Cn.union res_failures res_failures') in + Results.union res''' res_failures' + ensures { [@expl:args_incomplete] + forall sta'. + eval_list_expr stk (inp, ctx, sta) le (sta', Incomplete) -> + Results.mem {state=sta'; context=ctx; data=()} BIncomplete result + } + ensures { [@expl:utility_incomplete] + forall sta' ss sta''. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + interp_utility (utility_context ss ctx, id, sta') (sta'', Incomplete) -> + Results.mem {state=sta''; context=ctx; data=()} BIncomplete result + by Results.mem {state=sta''; context=ctx; data=()} BIncomplete res_failures' + } + ensures { [@expl:utility] + forall sta' ss sta'' b. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + interp_utility (utility_context ss ctx, id, sta') (sta'', Ok b) -> + let ctx' = {ctx with result=b} in + Results.mem {state=sta''; context=ctx'; data=()} (behaviour inp b) result + by Cn.mem {state=sta''; context=ctx'; data=()} res'' + } + + | ICallFunction id le -> + let arg_res, arg_res_failures = separate_results - (interp_str_expr stk b1 inp ctx sta1 se2) + (interp_list_expr stk inp ctx sta le) in - let res2_failure' = - Cn.map (fun sta -> sta, Incomplete) res2_failure + assert { [@expl:callfun1] + forall sta1 args. + eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> + Cn.mem (sta1, args) arg_res + }; + let res = + match ctx.func_env[id] with + | Some ins -> (* Function defined as `ins` *) + if stk = get_finite inp.config.stack_size then (* Stack overflow *) + let aux arg = + let sta', _ = arg in + let sta' = { sta' with log = Stdout.(newline (output stack_limit_message sta'.log)) } in + {state=sta'; context=ctx; data=()} + in + Results.({empty with failure = Cn.map aux arg_res}) + ensures { [@expl:callfun2] + forall sta1 args ins. + eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> + ctx.func_env[id] = Some ins -> + inp.config.stack_size = Finite stk -> + let sta1' = { sta1 with log = Stdout.(newline (output stack_limit_message sta1.log)) } in + Results.mem {state=sta1'; context=ctx; data=()} BIncomplete result + } + else (* Execute function *) + let res2 = + let function aux (arg: (state, list string)) = + let sta1, args = arg in + let inp1 = { inp with argument0 = identifier_to_string id } in + let ctx1 = { ctx with arguments = args } in + interp_instr (stk+1) inp1 ctx1 sta1 ins + in + Results.bind_collection aux arg_res + ensures { [@expl:callfun3] + forall sta1 args ins sta2 ctx2 bhv2. + eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> + ctx.func_env[id] = Some ins -> + inp.config.stack_size <> Finite stk -> + let inp1 = { inp with argument0 = identifier_to_string id } in + let ctx1 = { ctx with arguments = args } in + eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> + Results.mem {state=sta2; context=ctx2; data=()} bhv2 result + } + in + let res2' = + let function aux (sta2: sym_state unit) = + {state=sta2.state; context={sta2.context with arguments = ctx.arguments}; data=()} + in + Results.map aux res2 + ensures { [@expl:callfun4] + forall sta1 args ins sta2 ctx2 bhv2. + eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> + ctx.func_env[id] = Some ins -> + inp.config.stack_size <> Finite stk -> + let inp1 = { inp with argument0 = identifier_to_string id } in + let ctx1 = { ctx with arguments = args } in + eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> + Results.mem {state=sta2; context={ctx2 with arguments = ctx.arguments}; data=()} bhv2 result + } + in + Results.({res2' with normal=Cn.union res2'.normal res2'.return_; return_=Cn.empty}) + ensures { [@expl:callfun5] + forall sta1 args ins sta2 ctx2 bhv2. + eval_list_expr stk (inp, ctx, sta) le (sta1, Ok args) -> + ctx.func_env[id] = Some ins -> + inp.config.stack_size <> Finite stk -> + let inp1 = {inp with argument0=identifier_to_string id} in + let ctx1 = {ctx with arguments=args} in + eval_instruction (stk+1) (inp1, ctx1, sta1) ins (sta2, ctx2, bhv2) -> + let bhv' = match bhv2 with BNormal | BReturn -> BNormal | BExit -> BExit | BIncomplete -> BIncomplete end in + let ctx' = { ctx2 with arguments = ctx.arguments } in + Results.mem {state=sta2; context=ctx'; data=()} bhv' result + } + | None -> (* Undefined function *) + let arg_res' = + let function aux (arg: (state, list string)) = + let sta', _ = arg in + {state=sta'; context={ctx with result=False}; data=()} + in + Cn.map aux arg_res + ensures { + forall sta' ss. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + ctx.func_env[id] = None -> + Cn.mem {state=sta'; context={ctx with result=False}; data=()} result + by Cn.mem (sta', ss) arg_res + } + in + Results.single_behaviour (behaviour inp False) arg_res' + ensures { + forall sta' ss. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + ctx.func_env[id] = None -> + Results.mem {state=sta'; context={ctx with result=False}; data=()} (behaviour inp False) result + } + end + in + let res_failures = Results.stas_as_failures ctx arg_res_failures in + Results.union res res_failures + + | IShift bn -> + match shift_arguments (option_get (mk_nat 1) bn).nat ctx.arguments with + | Some args -> + let ctx' = {ctx with result = True; arguments = args} in + let sta' = {state = sta; context = ctx'; data=()} in + Results.({empty with normal = Cn.singleton sta'}) + | None -> + let ctx' = {ctx with result = False} in + let sta' = {state = sta; context = ctx'; data=()} in + Results.inject inp (Cn.singleton sta') + end + + | IForeach id le ins -> + let lst_res, lst_res_failures = + separate_results + (interp_list_expr stk inp ctx sta le) + in + assert { + forall sta' ss. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + Cn.mem (sta', ss) lst_res + }; + let res_loop_cont = + (* Execute foreach loop on a result from interp_list_expr *) + let function aux arg = + let sta', ss = arg in + interp_foreach True stk inp ctx sta' id ss ins + in + Results.bind_collection aux lst_res + ensures { + forall sta' ss sta'' ctx' bhv b. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + eval_foreach stk True (inp, ctx, sta') id ss ins (sta'', ctx', bhv) b -> + Results.mem {state=sta''; context=ctx'; data=b} bhv result + } + in + let res_loop_cont' = + let function aux sta'' = + let ctx'' = {sta''.context with result=sta''.data} in + with_data () {sta'' with context = ctx''} + in + Results.map aux res_loop_cont + ensures { + forall sta' ss sta'' ctx' bhv b. + eval_list_expr stk (inp, ctx, sta) le (sta', Ok ss) -> + eval_foreach stk True (inp, ctx, sta') id ss ins (sta'', ctx', bhv) b -> + + Results.mem {state=sta''; context={ctx' with result=b}; data=()} bhv result + } + in + let res_failure = + Results.stas_as_failures ctx lst_res_failures + ensures { + forall sta'. + eval_list_expr stk (inp, ctx, sta) le (sta', Incomplete) -> + Results.mem {state=sta'; context=ctx; data=()} BIncomplete result + } + in + Results.(union res_loop_cont' res_failure) + + | IWhile ins1 ins2 -> + let res = interp_while True 0 stk inp ctx sta ins1 ins2 in + let function aux_normal (sta': sym_state (int, bool)) = + let _, b = sta'.data in + {state=sta'.state; context={sta'.context with result=b}; data=()} + in + let function aux_other (sta': sym_state (int, bool)) = + {sta' with data=()} + in + Results.({normal=Cn.map aux_normal res.normal; + exit=Cn.map aux_other res.exit; + return_=Cn.map aux_other res.return_; + failure=Cn.map aux_other res.failure}) + ensures { + forall sta' ctx' bhv ctr b. + bhv <> BNormal -> + eval_while stk 0 True (inp, ctx, sta) ins1 ins2 (sta', ctx', bhv) ctr b -> + Results.mem {state=sta'; context=ctx'; data=()} bhv result + } + ensures { + forall sta' ctx' ctr b. + eval_while stk 0 True (inp, ctx, sta) ins1 ins2 (sta', ctx', BNormal) ctr b -> + Results.mem {state=sta'; context={ctx' with result=b}; data=()} BNormal result + } + + | IPipe ins1 ins2 -> + let stas1, stas1_failure = + Results.separate_non_failure + (interp_instr stk inp ctx {sta with stdout=Stdout.empty} ins1) + in + assert { + forall sta1 ctx1 bhv1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + Cn.mem {state=sta1; context=ctx1; data=()} stas1 + }; + let stas1': Cn.t (sym_state stdin) = (* data is sta1.state.stdin *) + let function aux (sta1: sym_state unit) = + {state={sta1.state with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.state.stdout}; context=ctx; data=sta1.state.stdin} + in + Cn.map aux stas1 + ensures { + forall sta1 ctx1 bhv1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + Cn.mem + {state={sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}; context=ctx; data=sta1.stdin} + result + } + in + let res2: Results.t stdin = (* data is sta1.state.stdin *) + interp_instr' stk inp stas1' ins2 + ensures { + forall sta1 ctx1 bhv1 sta2 ctx2 bhv2. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + eval_instruction stk (inp, ctx, {sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}) ins2 (sta2, ctx2, bhv2) -> + Results.mem {state=sta2; context=ctx2; data=sta1.stdin} bhv2 result + } in let res2' = - let for_res2 arg2 = - let sta2, (str2, b2) = arg2 in - sta2, Ok (String.concat str1 str2, b2) + let function aux sta2 = + {state={sta2.state with stdin=sta2.data}; + context={ctx with result=sta2.context.result}; + data=()} in - Cn.map for_res2 res2 + Results.map aux res2 + ensures { + forall sta1 ctx1 bhv1 sta2 ctx2 bhv2. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + eval_instruction stk (inp, ctx, {sta1 with stdout=sta.stdout; stdin=Stdout.to_stdin sta1.stdout}) ins2 (sta2, ctx2, bhv2) -> + Results.mem {state={sta2 with stdin=sta1.stdin}; context={ctx with result=ctx2.result}; data=()} bhv2 result + } in - Cn.(union res2' res2_failure') - in - Cn.bind for_res1 res1 - in - let res1_failure' = - Cn.map (fun sta -> sta, Incomplete) res1_failure - in - Cn.(union res1' res1_failure') - end + let sta1_failure' = + let function aux (sta1: sym_state unit) = + {state={sta1.state with stdout=sta.stdout}; context=ctx; data=()} + in + Cn.map aux stas1_failure + ensures { + forall sta1 ctx1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins1 (sta1, ctx1, BIncomplete) -> + Cn.mem {state={sta1 with stdout=sta.stdout}; context=ctx; data=()} result + } + in + Results.(union res2' {empty with failure=sta1_failure'}) + + | IExit code -> + let r = + match code with + | RPrevious -> ctx.result + | RSuccess -> True + | RFailure -> False + end + in + let sta = {state=sta; context={ctx with result=r}; data=()} in + Results.({empty with exit = Cn.singleton sta}) + + | IReturn code -> + let r = + match code with + | RPrevious -> ctx.result + | RSuccess -> True + | RFailure -> False + end + in + let sta = + let ctx' = {ctx with result = r} in + {state = sta; context = ctx'; data=()} + in + Results.({empty with return_ = Cn.singleton sta}) + end + + with interp_foreach (b:bool) (stk:int) (inp:input) (ctx:context) (sta:state) (id:identifier) (ss:list string) (ins:instruction) : Results.t bool + variant { get_finite inp.config.stack_size - stk, size_instr ins, L.length ss } + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { stk <= get_finite inp.config.stack_size } + ensures { + forall sta' ctx' bhv b'. + eval_foreach stk b (inp, ctx, sta) id ss ins (sta', ctx', bhv) b' -> + Results.mem {state=sta'; context=ctx'; data=b'} bhv result + } + = match ss with + | Nil -> + Results.({empty with normal=Cn.singleton {state=sta; context=ctx; data=b}}) + | Cons s ss' -> + let res1_normal, res1_other = + Results.separate_normal + (interp_instr stk inp {ctx with var_env = ctx.var_env[id <- s]'} sta ins) + in + let res_normal' = + let function aux (sta1: sym_state unit) = + interp_foreach sta1.context.result stk inp sta1.context sta1.state id ss' ins + in + Results.bind_collection aux res1_normal + ensures { + forall sta1 ctx1 sta2 ctx2 bhv2 b2. + eval_instruction stk (inp, {ctx with var_env=ctx.var_env[id <- s]'}, sta) ins (sta1, ctx1, BNormal) -> + eval_foreach stk ctx1.result (inp, ctx1, sta1) id ss' ins (sta2, ctx2, bhv2) b2 -> + Results.mem {state=sta2; context=ctx2; data=b2} bhv2 result + } + in + let res1_other' = + let function aux (sta: sym_state unit) = + with_data sta.context.result sta + in + Results.map aux res1_other + ensures { + forall sta1 ctx1 bhv1. + eval_instruction stk (inp, {ctx with var_env=ctx.var_env[id <- s]'}, sta) ins (sta1, ctx1, bhv1) -> + bhv1 <> BNormal -> + Results.mem {state=sta1; context=ctx1; data=ctx1.result} bhv1 result + } + in + Results.union res_normal' res1_other' + end - with interp_list_expr (stk:int) (inp:input) (ctx:context) (sta:state) (le:list_expression) - : Cn.t (state, result (list string)) - variant { get_finite inp.config.stack_size - stk, size_list_expr le, -1 } - requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } - requires { stk <= get_finite inp.config.stack_size } - ensures { (* Over-approximation *) - forall sta' res. - eval_list_expr stk (inp, ctx, sta) le (sta', res) -> - Cn.mem (sta', res) result - } - = match le with - | Nil -> - Cn.singleton (sta, Ok Nil) - | Cons (se, sp) le' -> - let str_res1, str_res1_failures = - separate_results - (interp_str_expr stk True inp ctx sta se : Cn.t (state, result (string, bool))) - in - let res2 = - let for_sta_str (arg: (state, (string, bool))) + with interp_while b ctr stk inp ctx (sta:state) ins1 ins2 : Results.t (int, bool) + variant { get_finite inp.config.stack_size - stk, size_instr ins1+size_instr ins2, get_finite inp.config.loop_limit - ctr } + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { 0 <= ctr <= get_finite inp.config.loop_limit } + requires { stk <= get_finite inp.config.stack_size } + ensures { + forall sta' ctx' bhv ctr' b'. + eval_while stk ctr b (inp, ctx, sta) ins1 ins2 (sta', ctx', bhv) ctr' b' -> + Results.mem {state=sta'; context=ctx'; data=(ctr', b')} bhv result + } + = let loop_limit = get_finite inp.config.loop_limit in + if ctr = loop_limit then + let sta = { sta with log = Stdout.(newline (output loop_limit_message sta.log)) } in + Results.({empty with failure=Cn.singleton {state=sta; context=ctx; data=(ctr, b)}}) + else + let res1_normal, res1_abort = + Results.separate_normal + (interp_instr stk {inp with under_condition=True} ctx sta ins1) + in + assert { + forall sta1 ctx1. + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + Cn.mem {state=sta1; context=ctx1; data=()} res1_normal + }; + assert { + forall sta1 ctx1 bhv1. + bhv1 <> BNormal -> + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, bhv1) -> + Results.mem {state=sta1; context=ctx1; data=()} bhv1 res1_abort + }; + let res1_true, res1_false = + Cn.partition (fun sta -> sta.context.result) res1_normal + in + assert { + forall sta1 ctx1. + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = True -> + Cn.mem {state=sta1; context=ctx1; data=()} res1_true + }; + assert { + forall sta1 ctx1. + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = False -> + Cn.mem {state=sta1; context=ctx1; data=()} res1_false + }; + let res2_normal, res2_abort = + Results.separate_normal + (interp_instr' stk inp res1_true ins2) + in + assert { + forall sta1 ctx1 sta2 ctx2. + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = True -> + eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, BNormal) -> + Cn.mem {state=sta2; context=ctx2; data=()} res2_normal + }; + assert { + forall sta1 ctx1 sta2 ctx2 bhv2. + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = True -> + eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, bhv2) -> + bhv2 <> BNormal -> + Results.mem {state=sta2; context=ctx2; data=()} bhv2 res2_abort + }; + let res3 = + let function aux (sta2:sym_state unit) = + interp_while sta2.context.result (ctr+1) stk inp sta2.context sta2.state ins1 ins2 + in + Results.bind_collection aux res2_normal + ensures { + forall sta1 ctx1 sta2 ctx2 sta3 ctx3 bhv3 n b'. + inp.config.loop_limit <> Finite ctr -> + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = True -> + eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, BNormal) -> + eval_while stk (ctr+1) ctx2.result (inp, ctx2, sta2) ins1 ins2 (sta3, ctx3, bhv3) n b' -> + Results.mem {state=sta3;context=ctx3;data=(n, b')} bhv3 result + } + in + let function with_ctr_b (sta:sym_state unit) = + {sta with data=(ctr, b)} + in + let res1_abort' = + Results.map with_ctr_b res1_abort + ensures { + forall sta1 ctx1 bhv1. + inp.config.loop_limit <> Finite ctr -> + bhv1 <> BNormal -> + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, bhv1) -> + Results.mem {state=sta1;context=ctx1;data=(ctr, b)} bhv1 result + } + in + let res1_false' = + Results.({empty with normal=Cn.map with_ctr_b res1_false}) + ensures { + forall sta1 ctx1. + inp.config.loop_limit <> Finite ctr -> + ctx1.result = False -> + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + Results.mem {state=sta1;context=ctx1;data=(ctr, b)} BNormal result + } + in + let res2_abort' = + Results.map with_ctr_b res2_abort + ensures { + forall sta1 ctx1 sta2 ctx2 bhv2. + inp.config.loop_limit <> Finite ctr -> + eval_instruction stk ({inp with under_condition = True}, ctx, sta) ins1 (sta1, ctx1, BNormal) -> + ctx1.result = True -> + eval_instruction stk (inp, ctx1, sta1) ins2 (sta2, ctx2, bhv2) -> + bhv2 <> BNormal -> + Results.mem {state=sta2;context=ctx2;data=(ctr, b)} bhv2 result + } + in + Results.(union res3 (union res1_abort' (union res1_false' res2_abort'))) + + with interp_instr' (stk:int) (inp:input) (stas: Cn.t (sym_state 'a)) (ins:instruction) : Results.t 'a + variant { get_finite inp.config.stack_size - stk, size_instr ins, Cn.size stas } + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { stk <= get_finite inp.config.stack_size } + ensures { (* Over-approximation *) + forall sta sta' ctx' bhv. + Cn.mem sta stas -> + eval_instruction stk (inp, sta.context, sta.state) ins (sta', ctx', bhv) -> + Results.mem {state=sta'; context=ctx'; data=sta.data} bhv result + } + = let interp_instr_with_data sta = ensures { - let sta1, (str1, b1) = arg in - forall sta2. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (str1, b1)) -> - eval_list_expr stk (inp, ctx, sta1) le' (sta2, Incomplete) -> - Cn.mem (sta2, Incomplete) result + forall sta' ctx' bhv. + eval_instruction stk (inp, sta.context, sta.state) ins (sta', ctx', bhv) -> + Results.mem {state=sta'; context=ctx'; data=sta.data} bhv result + } + Results.map (with_data sta.data) + (interp_instr stk inp sta.context sta.state ins) + in + Results.bind_collection interp_instr_with_data stas + + with interp_str_expr (stk:int) (b:bool) (inp:input) (ctx:context) (sta:state) (se:string_expression) + : Cn.t (state, result (string, bool)) + variant { get_finite inp.config.stack_size - stk, size_string_expr se, -1 } + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { stk <= get_finite inp.config.stack_size } + ensures { (* Over-approximation *) + forall sta' res. + eval_str_expr stk b (inp, ctx, sta) se (sta', res) -> + Cn.mem (sta', res) result + } + = match se with + | SLiteral str -> + Cn.singleton (sta, Ok (str, b)) + | SVariable var -> + let str = ctx.var_env[var]' in + Cn.singleton (sta, Ok (str, b)) + | SArgument n -> + let str = nth_argument (Cons inp.argument0 ctx.arguments) n.nat in + Cn.singleton (sta, Ok (str, b)) + | SSubshell ins -> + let res, stas_failure = + Results.separate_non_failure + (interp_instr stk inp ctx {sta with stdout=Stdout.empty} ins) + in + assert { + forall sta1 ctx1 bhv1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + Cn.mem {state=sta1; context=ctx1; data=()} res + }; + assert { + forall sta1 ctx1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, BIncomplete) -> + Cn.mem {state=sta1; context=ctx1; data=()} stas_failure + }; + let res' = + let for_res (sta1:sym_state unit) = + let str = Stdout.to_string sta1.state.stdout in + let sta1' = {sta1.state with stdout=sta.stdout} in + let b' = sta1.context.result in + sta1', Ok (str, b') + in + Cn.map for_res res + in + let res_failure' = + Cn.map (fun sta1 -> {sta1.state with stdout=sta.stdout}, Incomplete) stas_failure + in + Cn.union res' res_failure' + ensures { + forall sta1 ctx1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, BIncomplete) -> + Cn.mem ({sta1 with stdout = sta.stdout}, Incomplete) result + by Cn.mem ({sta1 with stdout = sta.stdout}, Incomplete) res_failure' } ensures { - let sta1, (str1, b1) = arg in - forall sta2 l2. - eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (str1, b1)) -> - eval_list_expr stk (inp, ctx, sta1) le' (sta2, Ok l2) -> - Cn.mem (sta2, Ok L.(split sp str1++l2)) result + forall sta1 ctx1 bhv1. + eval_instruction stk (inp, ctx, {sta with stdout=Stdout.empty}) ins (sta1, ctx1, bhv1) -> + bhv1 <> BIncomplete -> + Cn.mem ({sta1 with stdout = sta.stdout}, Ok (Stdout.to_string sta1.stdout, ctx1.result)) result + by Cn.mem ({sta1 with stdout = sta.stdout}, Ok (Stdout.to_string sta1.stdout, ctx1.result)) res' } - = let sta1, (str1, _) = arg in - let lst_res2: Cn.t (state, list string), lst_res2_failures: Cn.t state = + | SConcat se1 se2 -> + let res1, res1_failure = separate_results - (interp_list_expr stk inp ctx sta1 le') + (interp_str_expr stk b inp ctx sta se1) in - let lst_res2' = - Cn.map (fun arg -> let sta2, strs2 = arg in sta2, Ok L.(split sp str1++strs2)) lst_res2 + let res1' = + let for_res1 arg + ensures { + let sta1, (str1, b1) = arg in + forall sta2. + eval_str_expr stk b (inp, ctx, sta) se1 (sta1, Ok (str1, b1)) -> + eval_str_expr stk b1 (inp, ctx, sta1) se2 (sta2, Incomplete) -> + Cn.mem (sta2, Incomplete) result + } + ensures { + let sta1, (str1, b1) = arg in + forall sta2 str2 b2. + eval_str_expr stk b (inp, ctx, sta) se1 (sta1, Ok (str1, b1)) -> + eval_str_expr stk b1 (inp, ctx, sta1) se2 (sta2, Ok (str2, b2)) -> + Cn.mem (sta2, Ok (String.concat str1 str2, b2)) result + } + = let sta1, (str1, b1) = arg in + let res2, res2_failure = + separate_results + (interp_str_expr stk b1 inp ctx sta1 se2) + in + let res2_failure' = + Cn.map (fun sta -> sta, Incomplete) res2_failure + in + let res2' = + let for_res2 arg2 = + let sta2, (str2, b2) = arg2 in + sta2, Ok (String.concat str1 str2, b2) + in + Cn.map for_res2 res2 + in + Cn.(union res2' res2_failure') + in + Cn.bind for_res1 res1 in - let lst_res2_failures' = - Cn.map (fun sta -> sta, Incomplete) lst_res2_failures + let res1_failure' = + Cn.map (fun sta -> sta, Incomplete) res1_failure in - Cn.(union lst_res2' lst_res2_failures') - in - Cn.bind for_sta_str str_res1 - in - let str_res1_failures' = - Cn.map (fun sta -> sta, Incomplete) str_res1_failures - in - Cn.(union res2 str_res1_failures') - end + Cn.(union res1' res1_failure') + end - let rec interp_function_definitions (fenv:func_env) (defs:list function_definition) - variant { defs } - = match defs with - | Nil -> fenv - | Cons (id, instr) defs' -> - interp_function_definitions fenv[id <- Some instr] defs' - end + with interp_list_expr (stk:int) (inp:input) (ctx:context) (sta:state) (le:list_expression) + : Cn.t (state, result (list string)) + variant { get_finite inp.config.stack_size - stk, size_list_expr le, -1 } + requires { inp.config.loop_limit <> Infinite /\ inp.config.stack_size <> Infinite } + requires { stk <= get_finite inp.config.stack_size } + ensures { (* Over-approximation *) + forall sta' res. + eval_list_expr stk (inp, ctx, sta) le (sta', res) -> + Cn.mem (sta', res) result + } + = match le with + | Nil -> + Cn.singleton (sta, Ok Nil) + | Cons (se, sp) le' -> + let str_res1, str_res1_failures = + separate_results + (interp_str_expr stk True inp ctx sta se : Cn.t (state, result (string, bool))) + in + let res2 = + let for_sta_str (arg: (state, (string, bool))) + ensures { + let sta1, (str1, b1) = arg in + forall sta2. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (str1, b1)) -> + eval_list_expr stk (inp, ctx, sta1) le' (sta2, Incomplete) -> + Cn.mem (sta2, Incomplete) result + } + ensures { + let sta1, (str1, b1) = arg in + forall sta2 l2. + eval_str_expr stk True (inp, ctx, sta) se (sta1, Ok (str1, b1)) -> + eval_list_expr stk (inp, ctx, sta1) le' (sta2, Ok l2) -> + Cn.mem (sta2, Ok L.(split sp str1++l2)) result + } + = let sta1, (str1, _) = arg in + let lst_res2: Cn.t (state, list string), lst_res2_failures: Cn.t state = + separate_results + (interp_list_expr stk inp ctx sta1 le') + in + let lst_res2' = + Cn.map (fun arg -> let sta2, strs2 = arg in sta2, Ok L.(split sp str1++strs2)) lst_res2 + in + let lst_res2_failures' = + Cn.map (fun sta -> sta, Incomplete) lst_res2_failures + in + Cn.(union lst_res2' lst_res2_failures') + in + Cn.bind for_sta_str str_res1 + in + let str_res1_failures' = + Cn.map (fun sta -> sta, Incomplete) str_res1_failures + in + Cn.(union res2 str_res1_failures') + end + + let rec interp_function_definitions (fenv:func_env) (defs:list function_definition) + variant { defs } + = match defs with + | Nil -> fenv + | Cons (id, instr) defs' -> + interp_function_definitions fenv[id <- Some instr] defs' + end - let function only_states_with_result (res: bool) (stas: Cn.t (sym_state unit)) : Cn.t state = - Cn.(map (fun sta -> sta.state) - (filter (fun sta -> notb (xorb res sta.context.result)) - stas)) - - let interp_program inp stas pro : (Cn.t state, Cn.t state, Cn.t state) - requires { inp.config.loop_limit <> Infinite } - requires { inp.config.stack_size <> Infinite } - = let stas, stas_failure = - let stas = - let aux sta = - let fenv = interp_function_definitions sta.context.func_env pro.function_definitions in - {sta with context={sta.context with func_env = fenv}} + let function only_states_with_result (res: bool) (stas: Cn.t (sym_state unit)) : Cn.t state = + Cn.(map (fun sta -> sta.state) + (filter (fun sta -> notb (xorb res sta.context.result)) + stas)) + + let interp_program inp stas pro : (Cn.t state, Cn.t state, Cn.t state) + requires { inp.config.loop_limit <> Infinite } + requires { inp.config.stack_size <> Infinite } + = let stas, stas_failure = + let stas = + let aux sta = + let fenv = interp_function_definitions sta.context.func_env pro.function_definitions in + {sta with context={sta.context with func_env = fenv}} + in + Cn.map aux stas + in + Results.separate_non_failure + (interp_instr' 0 inp stas pro.instruction) in - Cn.map aux stas - in - Rs.separate_non_failure - (interp_instr' 0 inp stas pro.instruction) - in - (only_states_with_result True stas, - only_states_with_result False stas, - Cn.map (fun sta -> sta.state) stas_failure) + (only_states_with_result True stas, + only_states_with_result False stas, + Cn.map (fun sta -> sta.state) stas_failure) + end + end end \ No newline at end of file diff --git a/src/symbolic/symbolicUtility.ml b/src/symbolic/symbolicUtility.ml index 2fbf85a4..03c366c0 100644 --- a/src/symbolic/symbolicUtility.ml +++ b/src/symbolic/symbolicUtility.ml @@ -1,302 +1,346 @@ -open Colis_internals open Colis_constraints open Semantics__Result open Semantics__Buffers -open SymbolicInterpreter__Semantics - -type utility = state -> (state * bool result) list - -let return result : utility = - function sta -> [sta, Ok result] - -let apply_to_list l u = - (* apply utility [u] to a list [l] of states *) - List.concat (List.map u l) - -let separate_states l = - (* split a list of pairs state*bool into the list of states with flag - [true] and the list of pairs with flag [false] - *) - let rec separate_aux posacc negacc incacc = function - | [] -> (posacc,negacc,incacc) - | (s, Ok true)::l -> separate_aux (s::posacc) negacc incacc l - | (s, Ok false)::l -> separate_aux posacc (s::negacc) incacc l - | (_, Incomplete) as s::l -> separate_aux posacc negacc (s::incacc) l - in separate_aux [] [] [] l - -let choice u1 u2 = - (* non-deterministic choice *) - function state -> (u1 state) @ (u2 state) - -let if_then_else (cond:utility) (posbranch:utility) (negbranch:utility) = - function sta -> - let (posstates,negstates,incstates) = separate_states (cond sta) - in - (apply_to_list posstates posbranch) - @ (apply_to_list negstates negbranch) - @ incstates -let if_then (cond:utility) (posbranch:utility) = - function sta -> - let (posstates,negstates,incstates) = separate_states (cond sta) - in - (apply_to_list posstates posbranch) - @ (List.map (function sta -> (sta,Ok true)) negstates) - @ incstates - -let uneg (u:utility) : utility = fun st -> - List.map (function (s,Ok b) -> (s, Ok (not b)) | x -> x) (u st) - -let combine_results combinator u1 u2 : utility = fun st -> - List.flatten - (List.map - (function (s1,Ok b1) -> - List.map (function - | (s2,Ok b2) -> (s2,Ok(combinator b1 b2)) - | x -> x) (u2 s1) - | x -> [x]) - (u1 st)) - -let uand = - combine_results ( && ) - -let uor = - combine_results ( || ) - -let multiple_times what args : utility = - let rec aux = function - | [] -> assert false (* By precondition. *) - | [x] -> what x - | x :: xs -> uand (what x) (aux xs) - in - aux args - -let compose_non_strict (u1:utility) (u2:utility) = - function sta -> - apply_to_list (List.map fst (u1 sta)) u2 - -let compose_strict (u1:utility) (u2:utility) = - function sta -> - let (success1,failure1,incomplete1) = separate_states (u1 sta) - in (apply_to_list success1 u2) @ - (List.map (function sta -> (sta,Ok false)) failure1) @ - incomplete1 - -let print_output ~newline str output = - let output = Stdout.output str output in - if newline then Stdout.newline output else output - -let print_stdout ~newline str sta = - let stdout = print_output ~newline str sta.stdout in - let log = print_output ~newline str sta.log in - {sta with stdout; log} - -let print_error str sta = - let log = print_output ~newline:true ("[ERROR] "^str) sta.log in - {sta with log} - -let print_utility_trace str sta = - if String.equal str "" then - sta - else - let log = - print_output ~newline:true ("[TRACE] "^str) sta.log in - {sta with log} +module type FILESYSTEM = sig + type filesystem +end -let print_incomplete_trace str sta = - if String.equal str "" then - sta - else - let log = print_output ~newline:true ("[INCOMPLETE] "^str) sta.log in - {sta with log} +module type CASESPEC = sig -type case_spec = Var.t -> Var.t -> Clause.t - -let noop : case_spec = - Clause.eq - -type case = { - result : bool result; - spec : case_spec; - descr : string; - stdout : Stdout.t ; - error_message: string option; -} - -let success_case ~descr ?(stdout=Stdout.empty) spec = - { result = Ok true ; error_message = None ; stdout ; descr; spec } - -let error_case ~descr ?(stdout=Stdout.empty) ?error_message spec = - { result = Ok false ; error_message ; stdout ; descr ; spec } - -let incomplete_case ~descr spec = - { result = Incomplete ; - descr ; - stdout = Stdout.empty ; - error_message = None; - spec } - -(** Apply the case specifications to a filesystem, resulting in a list of possible filesystems. *) -let apply_spec fs spec = - let open SymbolicInterpreter__Filesystem in - let root_is_root0 = - (* fs.root0 = Some fs.root - garbare-collect fs.root only otherwise *) - match fs.root0 with - | Some root0 -> Var.equal root0 fs.root - | None -> false in - let root' = Var.fresh ~hint:(Var.hint fs.root) () in - let clause = spec fs.root root' in - let clauses = Clause.add_to_sat_conj clause fs.clause in - let clauses = - if root_is_root0 then - clauses - else - List.flatten - (List.map (Clause.quantify_over_and_simplify fs.root) - clauses) in - List.map (fun clause -> {fs with clause; root=root'}) clauses - -(** Apply a case to a state. - - This may result in multiple states because the integration of the case clause in the - filesystem may result in multiple clauses. *) -let apply_case sta case : (state * bool result) list = - (* First print the utility trace *) - let sta = - if case.result = Incomplete - then sta (* Print incomplete trace last *) - else print_utility_trace case.descr sta in - let sta = { - (* output case stdout to stdout and log *) - stdout = Stdout.concat sta.stdout case.stdout; - log = Stdout.concat sta.log case.stdout; - (* don't touch stdin *) - stdin = sta.stdin; - (* and keep the filesystem (may be changed by apply_spec) *) - filesystem = sta.filesystem; - } in - (* (Optionally) print error message *) - let sta = - match case.error_message with - | Some msg -> print_error msg sta - | None -> sta in - let sta = - if case.result = Incomplete - then print_incomplete_trace case.descr sta - else sta in - (* Apply the case specifications to the filesystem *) - apply_spec sta.filesystem case.spec |> - (* Inject the resulting filesystems into the state *) - List.map (fun filesystem -> {sta with filesystem}) |> - (* Add the result to each result state *) - List.map (fun sta -> sta, case.result) - -let specification_cases cases state = - List.flatten (List.map (apply_case state) cases) - -(******************************************************************************) -(* Auxiliaries *) -(******************************************************************************) - -let last_comp_as_hint: root:Var.t -> Path.t -> string option = - fun ~root path -> - match Path.split_last path with - | Some (_, Down f) -> - Some (Feat.to_string f) - | None -> (* Empty parent path => root *) - Some (Var.hint root) - | Some (_, (Here|Up)) -> - (* We can’t know (if last component in parent path is a symbolic link) *) - None + type case_spec + val noop : case_spec -let error ~utility msg : utility = - fun sta -> - let str = utility ^ ": " ^ msg in - let sta' = print_error str sta in - [ sta', Ok false ] + type filesystem + val apply_spec : filesystem -> case_spec -> filesystem list +end -let incomplete ~utility msg : utility = - fun sta -> - let str = utility ^ ": " ^ msg in - [print_incomplete_trace str sta, Incomplete] +module Make (Filesystem: FILESYSTEM) = struct -let unknown ~utility msg : utility = - match !Options.unknown_behaviour with - | Exception -> raise (Errors.Unknown_behaviour (utility, msg)) - | Incomplete -> incomplete ~utility msg - | Error -> error ~utility "not found" + module Semantics = SymbolicInterpreter__Interpreter.MakeSemantics (Filesystem) + open Semantics -module IdMap = Env.IdMap + type utility = state -> (state * bool result) list -type context = { - args: string list; - cwd: Path.normal; - env: string IdMap.t; -} + let print_output ~newline str output = + let output = Stdout.output str output in + if newline then Stdout.newline output else output -module type SYMBOLIC_UTILITY = sig - val name : string - val interprete : context -> utility -end + let print_stdout ~newline str sta = + let stdout = print_output ~newline str sta.stdout in + let log = print_output ~newline str sta.log in + {sta with stdout; log} -let table = - let table = Hashtbl.create 10 in - (* TODO Register all POSIX utilities as: incomplete ~descr:(name^": not implemented") *) - table + let print_error str sta = + let log = print_output ~newline:true ("[ERROR] "^str) sta.log in + {sta with log} + + let print_utility_trace str sta = + if String.equal str "" then + sta + else + let log = print_output ~newline:true ("[TRACE] "^str) sta.log in + {sta with log} -let register (module M:SYMBOLIC_UTILITY) = - Hashtbl.replace table M.name M.interprete + let print_incomplete_trace str sta = + let log = print_output ~newline:true ("[INCOMPLETE] "^str) sta.log in + {sta with log} -let is_registered = Hashtbl.mem table + let error ~utility msg : utility = + fun sta -> + let str = utility ^ ": " ^ msg in + let sta = print_error str sta in + [sta, Ok false] -let dispatch ~name = - try Hashtbl.find table name - with Not_found -> + let incomplete ~utility msg : utility = + fun sta -> + let str = utility ^ ": " ^ msg in + let sta = print_incomplete_trace str sta in + [sta, Incomplete] + + let unknown ~utility msg : utility = + let open Colis_internals in + match !Options.unknown_behaviour with + | Exception -> raise (Errors.Unknown_behaviour (utility, msg)) + | Incomplete -> incomplete ~utility msg + | Error -> error ~utility "not found" + + let table : (string, utility_context -> utility) Hashtbl.t = + let table = Hashtbl.create 10 in + (* TODO Register all POSIX utilities as: incomplete ~descr:(name^": not implemented") *) + table + + module type SYMBOLIC_UTILITY = sig + val name : string + val interprete : utility_context -> utility + end + + let register (module M: SYMBOLIC_UTILITY) = + Hashtbl.replace table M.name M.interprete + + let is_registered = Hashtbl.mem table + + let dispatch ~name = + try Hashtbl.find table name + with Not_found -> fun _ctx sta -> unknown ~utility:name "command not found" sta -let call name ctx args = - dispatch ~name {ctx with args} - -let dispatch' ((cwd, env, args), name, sta) = - let cwd = List.map Colis_constraints.Feat.from_string cwd in - let ctx = {cwd; args; env} in - BatSet.of_list (dispatch ~name ctx sta) - -(**/**) - -let null_formatter = Format.make_formatter (fun _ _ _ -> ()) (fun () -> ()) - -let with_formatter_to_string f = - let buf = Buffer.create 8 in - let fmt = Format.formatter_of_buffer buf in - let v =f fmt in - Format.pp_print_flush fmt (); - (Buffer.contents buf, v) - -let cmdliner_eval_utility ~utility ?(empty_pos_args=false) fun_and_args ctx = - let pos_args = Cmdliner.Arg.( - (if empty_pos_args then value else non_empty) - & pos_all string [] - & info []) - in - let argv = ctx.args |> List.cons utility |> Array.of_list in - let (err, result) = - with_formatter_to_string @@ fun err -> - Cmdliner.Term.( - eval - ( - fun_and_args $ const ctx $ pos_args, - info utility ~exits:default_exits - ) - ~argv - ~env:(fun var -> Env.IdMap.find_opt var ctx.env) - ~help:null_formatter ~err ~catch:false - ) - in - match result with - | `Ok a -> a - | `Version -> error ~utility "version" - | `Help -> error ~utility "help" - | `Error (`Parse | `Term) -> unknown ~utility ("parse error: " ^ err) - | `Error `Exn -> assert false (* because ~catch:false *) + module Arg = struct + let sym_interp_utility (ctx, name, sta) = + dispatch ~name ctx sta + end + + module Interpreter = MakeInterpreter (Arg) + + type sym_state = { + context : Semantics__Context.context; + state : Semantics.state; + } + + let interp_program inp stas pro = + let stas = List.map (fun {context; state} -> Interpreter.({context; state; data=()})) stas in + Interpreter.interp_program inp stas pro + + let call name ctx args = + dispatch ~name {ctx with args} + + let return result : utility = + function sta -> + [sta, Ok result] + + let apply_to_list l u = + (* apply utility [u] to a list [l] of states *) + List.concat (List.map u l) + + let separate_states l = + (* split a list of pairs state*bool into the list of states with flag + [true] and the list of pairs with flag [false] + *) + let rec separate_aux posacc negacc incacc = function + | [] -> (posacc,negacc,incacc) + | (s, Ok true)::l -> separate_aux (s::posacc) negacc incacc l + | (s, Ok false)::l -> separate_aux posacc (s::negacc) incacc l + | (_, Incomplete) as s::l -> separate_aux posacc negacc (s::incacc) l + in separate_aux [] [] [] l + + let choice u1 u2 = + (* non-deterministic choice *) + function state -> (u1 state) @ (u2 state) + + let if_then_else (cond:utility) (posbranch:utility) (negbranch:utility) = + function sta -> + let (posstates,negstates,incstates) = separate_states (cond sta) + in + (apply_to_list posstates posbranch) + @ (apply_to_list negstates negbranch) + @ incstates + + let if_then (cond:utility) (posbranch:utility) = + function sta -> + let (posstates,negstates,incstates) = separate_states (cond sta) + in + (apply_to_list posstates posbranch) + @ (List.map (function sta -> (sta,Ok true)) negstates) + @ incstates + + let uneg (u:utility) : utility = fun st -> + List.map (function (s,Ok b) -> (s, Ok (not b)) | x -> x) (u st) + + let combine_results combinator u1 u2 : utility = fun st -> + List.flatten + (List.map + (function (s1,Ok b1) -> + List.map (function + | (s2,Ok b2) -> (s2,Ok(combinator b1 b2)) + | x -> x) (u2 s1) + | x -> [x]) + (u1 st)) + + let uand = + combine_results ( && ) + + let uor = + combine_results ( || ) + + let multiple_times what args : utility = + let rec aux = function + | [] -> assert false (* By precondition. *) + | [x] -> what x + | x :: xs -> uand (what x) (aux xs) + in + aux args + + let compose_non_strict (u1:utility) (u2:utility) = + function sta -> + apply_to_list (List.map fst (u1 sta)) u2 + + let compose_strict (u1:utility) (u2:utility) = + function sta -> + let (success1,failure1,incomplete1) = separate_states (u1 sta) + in (apply_to_list success1 u2) @ + (List.map (function sta -> (sta,Ok false)) failure1) @ + incomplete1 + + (******************************************************************************) + (* Auxiliaries *) + (******************************************************************************) + + let null_formatter = Format.make_formatter (fun _ _ _ -> ()) (fun () -> ()) + + let with_formatter_to_string f = + let buf = Buffer.create 8 in + let fmt = Format.formatter_of_buffer buf in + let v =f fmt in + Format.pp_print_flush fmt (); + (Buffer.contents buf, v) + + let cmdliner_eval_utility ~utility ?(empty_pos_args=false) fun_and_args ctx = + let pos_args = Cmdliner.Arg.( + (if empty_pos_args then value else non_empty) + & pos_all string [] + & info []) + in + let argv = ctx.args |> List.cons utility |> Array.of_list in + let (err, result) = + with_formatter_to_string @@ fun err -> + Cmdliner.Term.( + eval + ( + fun_and_args $ const ctx $ pos_args, + info utility ~exits:default_exits + ) + ~argv + ~env:(fun var -> Env.SMap.find_opt var ctx.env) + ~help:null_formatter ~err ~catch:false + ) + in + match result with + | `Ok a -> a + | `Version -> error ~utility "version" + | `Help -> error ~utility "help" + | `Error (`Parse | `Term) -> unknown ~utility ("parse error: " ^ err) + | `Error `Exn -> assert false (* because ~catch:false *) + + (**/**) + + module MakeSpecifications (CaseSpec: CASESPEC with type filesystem = Filesystem.filesystem) = struct + + type case = { + result : bool result; + spec : CaseSpec.case_spec; + descr : string; + stdout : Stdout.t ; + error_message: string option; + } + + let success_case ~descr ?(stdout=Stdout.empty) spec = + { result = Ok true ; error_message = None ; stdout ; descr; spec } + + let error_case ~descr ?(stdout=Stdout.empty) ?error_message spec = + { result = Ok false ; error_message ; stdout ; descr ; spec } + + let incomplete_case ~descr spec = + { result = Incomplete ; + descr ; + stdout = Stdout.empty ; + error_message = None; + spec } + + (** Apply a case to a state. + + This may result in multiple states because the integration of the case clause in the + filesystem may result in multiple clauses. *) + let apply_case sta case : (state * bool result) list = + (* First print the utility trace *) + let sta = + if case.result = Incomplete + then sta (* Print incomplete trace last *) + else print_utility_trace case.descr sta in + let sta = { + (* output case stdout to stdout and log *) + stdout = Stdout.concat sta.stdout case.stdout; + log = Stdout.concat sta.log case.stdout; + (* don't touch stdin *) + stdin = sta.stdin; + (* and keep the filesystem (may be changed by apply_spec) *) + filesystem = sta.filesystem; + } in + (* (Optionally) print error message *) + let sta = + match case.error_message with + | Some msg -> print_error msg sta + | None -> sta in + let sta = + if case.result = Incomplete + then print_incomplete_trace case.descr sta + else sta in + (* Apply the case specifications to the filesystem *) + CaseSpec.apply_spec sta.filesystem case.spec |> + (* Inject the resulting filesystems into the state *) + List.map (fun filesystem -> {sta with filesystem}) |> + (* Add the result to each result state *) + List.map (fun sta -> sta, case.result) + + let specification_cases cases state = + List.flatten (List.map (apply_case state) cases) + end +end + +module SymbolicFilesystem = struct + type filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; + } +end + +module SymbolicCaseSpec = struct + + type case_spec = Var.t -> Var.t -> Clause.t + + let noop = Clause.eq + + type filesystem = SymbolicFilesystem.filesystem + + (** Apply the case specifications to a filesystem, resulting in a list of possible filesystems. *) + let apply_spec fs spec = + let open SymbolicFilesystem in + let root_is_root0 = + (* fs.root0 = Some fs.root - garbare-collect fs.root only otherwise *) + match fs.root0 with + | Some root0 -> Var.equal root0 fs.root + | None -> false in + let root' = Var.fresh ~hint:(Var.hint fs.root) () in + let clause = spec fs.root root' in + let clauses = Clause.add_to_sat_conj clause fs.clause in + let clauses = + if root_is_root0 then + clauses + else + List.flatten + (List.map (Clause.quantify_over_and_simplify fs.root) + clauses) in + List.map (fun clause -> {fs with clause; root=root'}) clauses +end + +module Symbolic = struct + include Make (SymbolicFilesystem) + include MakeSpecifications (SymbolicCaseSpec) + type context = Semantics.utility_context = { + cwd: Colis_constraints.Path.normal; + env: string Env.SMap.t; + args: string list; + } + let noop = SymbolicCaseSpec.noop + let last_comp_as_hint: root:Var.t -> Path.t -> string option = + fun ~root path -> + match Path.split_last path with + | Some (_, Down f) -> + Some (Feat.to_string f) + | None -> (* Empty parent path => root *) + Some (Var.hint root) + | Some (_, (Here|Up)) -> + (* We can’t know (if last component in parent path is a symbolic link) *) + None +end diff --git a/src/symbolic/symbolicUtility.mli b/src/symbolic/symbolicUtility.mli index 20bb8637..4bbff5fc 100644 --- a/src/symbolic/symbolicUtility.mli +++ b/src/symbolic/symbolicUtility.mli @@ -1,150 +1,190 @@ open Colis_constraints -open SymbolicInterpreter__Semantics open Semantics__Result open Semantics__Buffers -(** {1 Basics of symbolic utility} *) - -(** A utility transforms a symbolic state into a list of symbolic states - with boolean results *) -type utility = state -> (state * bool result) list - -(** The concrete evaluation context. It contains the fields from - Colis.Semantics.Context.context that are relevant to the utilities. *) -type context = { - args: string list; (** Command arguments *) - cwd: Path.normal; (** Current working directory *) - env: string Env.IdMap.t; (** Variable environment *) -} - -(** A symbolic utility is comprised of a name and a function that interpretes a [utility] - in a [context]. *) -module type SYMBOLIC_UTILITY = sig - val name : string - val interprete : context -> utility +module type FILESYSTEM = sig + type filesystem end -(** {1 Specifications of a symbolic utility} *) +module type CASESPEC = sig -(** A case in the specification is either a success or an error *) -type case + (** A case specification is a function from the old root variable and the new root root + variable to a clause. *) + type case_spec -(** A case specification is a function from the old root variable and the new root root - variable to a clause. *) -type case_spec = Var.t -> Var.t -> Clause.t + (** The no-op case specification introduces an equality constraint between the old root + and the new root. *) + val noop : case_spec -(** The no-op case specification introduces an equality constraint between the old root - and the new root. *) -val noop : case_spec + type filesystem + val apply_spec : filesystem -> case_spec -> filesystem list +end + +module Make (Filesystem: FILESYSTEM) : sig + + module Semantics : module type of SymbolicInterpreter__Interpreter.MakeSemantics (Filesystem) + open Semantics + + type sym_state = { + context : Semantics__Context.context; + state : Semantics.state; + } + + val interp_program : + Colis__Semantics__Input.input -> + sym_state list -> + Colis__Syntax__Syntax.program -> + Semantics.state list * Semantics.state list * Semantics.state list + + + (** {1 Basics of symbolic utility} *) + + (** A utility transforms a symbolic state into a list of symbolic states + with boolean results *) + type utility = state -> (state * bool result) list + + (** A symbolic utility is comprised of a name and a function that interpretes a [utility] + in a [context]. *) + module type SYMBOLIC_UTILITY = sig + val name : string + val interprete : utility_context -> utility + end -(** A success case (aka. return 0) *) -val success_case: descr:string -> ?stdout:Stdout.t -> case_spec -> case + (** {1 Registration} *) -(** An error case (aka return 1) *) -val error_case: descr:string -> ?stdout:Stdout.t -> ?error_message:string -> case_spec -> case + (** Register a symbolic utility *) + val register : (module SYMBOLIC_UTILITY) -> unit -(** An incomplete case (unknown behaviour, cannot be symbolically executed) *) -val incomplete_case: descr:string -> case_spec -> case + val is_registered : string -> bool -(** Use a list of cases to specify a utility. Corresponds to a table in the - document "Specification of UNIX Utilities "*) -val specification_cases : case list -> utility + (** {1 Dispatch} *) -(** {1 Registration} *) + (** A saner way to call [dispatch] (e.g. in the implementation of utilities) *) + val call : string -> utility_context -> string list -> utility -(** Register a symbolic utility *) -val register : (module SYMBOLIC_UTILITY) -> unit + (** {1 Combinators} *) -val is_registered : string -> bool + (** [choice u1 u2] yields the utility that non-deterministacillay + behaves like [u1] or [u2]. *) + val choice : utility -> utility -> utility -(** {1 Dispatch} *) + (** [return b] yields the utility that does not change its state, and + succeeds if and only if [b] is [true] *) + val return : bool -> utility -(** Entry-point for the interpretation of symbolic utilties *) -val dispatch : name:string -> context -> utility + (** [if_then_else u1 u2 u3] yields the utility that behaves like + if [u1] then [u2] else [u3] fi *) + val if_then_else : utility -> utility -> utility -> utility -(** A saner way to call [dispatch] (e.g. in the implementation of utilities) *) -val call : string -> context -> string list -> utility + (** [if_then u1 u2] yields the utility that behaves like + if [u1] then [u2] fi *) + val if_then : utility -> utility -> utility -(** {1 Combinators} *) + (** [uneg u] yields the utility that behaves like [u] but inverts its result *) + val uneg : utility -> utility -(** [choice u1 u2] yields the utility that non-deterministacillay - behaves like [u1] or [u2]. *) -val choice : utility -> utility -> utility + (** [uand u1 u2] yields the utility that behaves like [u1 && u2] in a NON-LAZY manner *) + val uand : utility -> utility -> utility -(** [return b] yields the utility that does not change its state, and - succeeds if and only if [b] is [true] *) -val return : bool -> utility + (** [uor u1 u2] yields the utility that behaves like [u1 || u2] in a NON-LAZY manner *) + val uor : utility -> utility -> utility -(** [if_then_else u1 u2 u3] yields the utility that behaves like - if [u1] then [u2] else [u3] fi *) -val if_then_else : utility -> utility -> utility -> utility + (** multiple_times [what] [args] executes [what] on every argument in + [args]. It does not stop if one of these executions fails but the + global utility fails in the case. *) + val multiple_times : ('a -> utility) -> 'a list -> utility -(** [if_then u1 u2] yields the utility that behaves like - if [u1] then [u2] fi *) -val if_then : utility -> utility -> utility + (** compose_non_strict [u1] [u2] yields the utility that behaves like + [u1]; [u2] in non-strict mode, that is the error code of [u1] is + ignored *) + val compose_non_strict : utility -> utility -> utility -(** [uneg u] yields the utility that behaves like [u] but inverts its result *) -val uneg : utility -> utility + (** compose_strict [u1] [u2] yields the utility that behaves like + [u1]; [u2] in strict mode, that is if [u1] fails then the composition + fails and [u2] is not executed *) + val compose_strict : utility -> utility -> utility -(** [uand u1 u2] yields the utility that behaves like [u1 && u2] in a NON-LAZY manner *) -val uand : utility -> utility -> utility + (** {1 Auxiliaries} *) -(** [uor u1 u2] yields the utility that behaves like [u1 || u2] in a NON-LAZY manner *) -val uor : utility -> utility -> utility + (** Error utility *) + val error : utility:string -> string -> utility -(** multiple_times [what] [args] executes [what] on every argument in - [args]. It does not stop if one of these executions fails but the - global utility fails in the case. *) -val multiple_times : ('a -> utility) -> 'a list -> utility + (** Unsupported stuff in a known utility. *) + val incomplete : utility:string -> string -> utility -(** compose_non_strict [u1] [u2] yields the utility that behaves like - [u1]; [u2] in non-strict mode, that is the error code of [u1] is - ignored *) -val compose_non_strict : utility -> utility -> utility + (* Unknown-unknown behaviour *) + val unknown : utility:string -> string -> utility -(** compose_strict [u1] [u2] yields the utility that behaves like - [u1]; [u2] in strict mode, that is if [u1] fails then the composition - fails and [u2] is not executed *) -val compose_strict : utility -> utility -> utility + (** {2 Printing} *) -(** {1 Auxiliaries} *) + (** Print to stdout and log *) + val print_stdout : newline:bool -> string -> state -> state -(** Error utility *) -val error : utility:string -> string -> utility + (** Print message as utility trace to log if it is not empty (marked as [UTL]) *) + val print_utility_trace : string -> state -> state -(** Unsupported stuff in a known utility. *) -val incomplete : utility:string -> string -> utility + (** {2 Arguments Parsing} *) -(* Unknown-unknown behaviour *) -val unknown : utility:string -> string -> utility + val cmdliner_eval_utility : + utility:string -> + ?empty_pos_args:bool -> + (utility_context -> string list -> utility) Cmdliner.Term.t -> + utility_context -> utility + (** A wrapper around [Cmdliner.Term.eval] for utilities. [utility] is the name + of the utility. [empty_pos_args] describe whether empty positional arguments + lists are accepted or not (refused by default). *) -(** {2 Printing} *) + (** {1 Specifications of a symbolic utility} *) -(** Print to stdout and log *) -val print_stdout : newline:bool -> string -> state -> state + module MakeSpecifications (CaseSpec: CASESPEC with type filesystem = Filesystem.filesystem) : sig -(** Print message as utility trace to log if it is not empty (marked as [UTL]) *) -val print_utility_trace : string -> state -> state + (** A case in the specification is either a success or an error *) + type case -(** {2 Path parsing}*) + (** A success case (aka. return 0) *) + val success_case: descr:string -> ?stdout:Stdout.t -> CaseSpec.case_spec -> case -(** Get the name of the last path component, if any, or of the hint - root variable otherwise. The result is useful as a hint for - creating variables for resolving the path. *) -val last_comp_as_hint: root:Var.t -> Path.t -> string option + (** An error case (aka return 1) *) + val error_case: descr:string -> ?stdout:Stdout.t -> ?error_message:string -> CaseSpec.case_spec -> case -(** {2 Arguments Parsing} *) + (** An incomplete case (unknown behaviour, cannot be symbolically executed) *) + val incomplete_case: descr:string -> CaseSpec.case_spec -> case -val cmdliner_eval_utility : - utility:string -> - ?empty_pos_args:bool -> - (context -> string list -> utility) Cmdliner.Term.t -> - context -> utility -(** A wrapper around [Cmdliner.Term.eval] for utilities. [utility] is the name - of the utility. [empty_pos_args] describe whether empty positional arguments - lists are accepted or not (refused by default). *) + (** Use a list of cases to specify a utility. Corresponds to a table in the + document "Specification of UNIX Utilities "*) + val specification_cases : case list -> utility + end +end + +module SymbolicFilesystem : sig + type filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; + } +end + +module SymbolicCaseSpec : CASESPEC with + type filesystem = SymbolicFilesystem.filesystem and + type case_spec = Var.t -> Var.t -> Clause.t + +(* Compatibility with previous SymbolicUtility *) +module Symbolic : sig -(**/**) + include module type of Make (SymbolicFilesystem) -(** A wrapper of [dispatch] for use in the Why3 driver *) -val dispatch' : ((string list * string Env.IdMap.t * string list) * string * state) -> (state * bool result) BatSet.t + include module type of MakeSpecifications (SymbolicCaseSpec) + + type context = Semantics.utility_context = { + cwd: Colis_constraints.Path.normal; + env: string Env.SMap.t; + args: string list; + } + + val noop : SymbolicCaseSpec.case_spec + + (** Get the name of the last path component, if any, or of the hint + root variable otherwise. The result is useful as a hint for + creating variables for resolving the path. *) + val last_comp_as_hint: root:Var.t -> Path.t -> string option +end diff --git a/src/symbolic/utilities/basics.ml b/src/symbolic/utilities/basics.ml index 58cff4ff..8f10a5a9 100644 --- a/src/symbolic/utilities/basics.ml +++ b/src/symbolic/utilities/basics.ml @@ -1,5 +1,4 @@ -open SymbolicUtility - +open SymbolicUtility.Symbolic module True = struct let name = "true" diff --git a/src/symbolic/utilities/colisInternalUnsafeTouch.ml b/src/symbolic/utilities/colisInternalUnsafeTouch.ml index 44c6a86d..c52e4447 100644 --- a/src/symbolic/utilities/colisInternalUnsafeTouch.ml +++ b/src/symbolic/utilities/colisInternalUnsafeTouch.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "colis_internal_unsafe_touch" diff --git a/src/symbolic/utilities/cp.ml b/src/symbolic/utilities/cp.ml index 33b8835b..4adce308 100644 --- a/src/symbolic/utilities/cp.ml +++ b/src/symbolic/utilities/cp.ml @@ -1,8 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility - +open SymbolicUtility.Symbolic let name = "cp" (* diff --git a/src/symbolic/utilities/cp.mli b/src/symbolic/utilities/cp.mli index 7cc30031..269cb613 100644 --- a/src/symbolic/utilities/cp.mli +++ b/src/symbolic/utilities/cp.mli @@ -1,4 +1,5 @@ (** Symbolic execution of cp *) +open SymbolicUtility.Symbolic val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/dpkg.ml b/src/symbolic/utilities/dpkg.ml index 8121487f..19dd5340 100644 --- a/src/symbolic/utilities/dpkg.ml +++ b/src/symbolic/utilities/dpkg.ml @@ -1,4 +1,4 @@ -open SymbolicUtility +open SymbolicUtility.Symbolic open Semantics__Result let name = "dpkg" diff --git a/src/symbolic/utilities/dpkgMaintscriptHelper.ml b/src/symbolic/utilities/dpkgMaintscriptHelper.ml index 8db76ea4..85fdb309 100644 --- a/src/symbolic/utilities/dpkgMaintscriptHelper.ml +++ b/src/symbolic/utilities/dpkgMaintscriptHelper.ml @@ -1,7 +1,7 @@ (** Symbolic execution of dpkg-maintscript-helper. The structure of this code follows the implementation of version 1.19.6. *) -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "dpkg-maintscript-helper" @@ -429,19 +429,19 @@ let interprete ctx = sub-commands except 'supports' has been factored out into the main code. *) let dms_package = - try Env.IdMap.find "DPKG_MAINTSCRIPT_PACKAGE" ctx.env + try Env.SMap.find "DPKG_MAINTSCRIPT_PACKAGE" ctx.env with Not_found -> raise (Error "environment variable DPKG_MAINTSCRIPT_PACKAGE is required") in let default_package = try - let dpkg_arch = Env.IdMap.find "DPKG_MAINTSCRIPT_ARCH" ctx.env + let dpkg_arch = Env.SMap.find "DPKG_MAINTSCRIPT_ARCH" ctx.env in dms_package^":"^dpkg_arch with Not_found -> dms_package in let dms_name = - try Env.IdMap.find "DPKG_MAINTSCRIPT_NAME" ctx.env + try Env.SMap.find "DPKG_MAINTSCRIPT_NAME" ctx.env with | Not_found -> raise (Error diff --git a/src/symbolic/utilities/dpkgMaintscriptHelper.mli b/src/symbolic/utilities/dpkgMaintscriptHelper.mli index 5b26b47b..93aaf109 100644 --- a/src/symbolic/utilities/dpkgMaintscriptHelper.mli +++ b/src/symbolic/utilities/dpkgMaintscriptHelper.mli @@ -1,4 +1,5 @@ (** Symbolic execution of dpkg-maintscript-helper *) +open SymbolicUtility.Symbolic val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/emacsPackage.ml b/src/symbolic/utilities/emacsPackage.ml index 1bba2484..5a017050 100644 --- a/src/symbolic/utilities/emacsPackage.ml +++ b/src/symbolic/utilities/emacsPackage.ml @@ -1,5 +1,5 @@ open Format -open SymbolicUtility +open SymbolicUtility.Symbolic module Install = struct let name = "/usr/lib/emacsen-common/emacs-package-install" diff --git a/src/symbolic/utilities/emacsPackage.mli b/src/symbolic/utilities/emacsPackage.mli index c7eba2ee..789ced53 100644 --- a/src/symbolic/utilities/emacsPackage.mli +++ b/src/symbolic/utilities/emacsPackage.mli @@ -1,13 +1,14 @@ (** Symbolic execution of emacs-package-[install|remove] *) +open SymbolicUtility.Symbolic module Install : sig val name : string - val interprete : SymbolicUtility.context -> SymbolicUtility.utility + val interprete : context -> utility end module Remove : sig val name : string - val interprete : SymbolicUtility.context -> SymbolicUtility.utility + val interprete : context -> utility end diff --git a/src/symbolic/utilities/mkdir.ml b/src/symbolic/utilities/mkdir.ml index 8afbed82..79b75ee8 100644 --- a/src/symbolic/utilities/mkdir.ml +++ b/src/symbolic/utilities/mkdir.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "mkdir" diff --git a/src/symbolic/utilities/mkdir.mli b/src/symbolic/utilities/mkdir.mli index 72649baf..84073e65 100644 --- a/src/symbolic/utilities/mkdir.mli +++ b/src/symbolic/utilities/mkdir.mli @@ -1,4 +1,5 @@ (** Symbolic execution of mkdir *) +open SymbolicUtility.Symbolic val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/mv.ml b/src/symbolic/utilities/mv.ml index e34c5b5d..44c071b0 100644 --- a/src/symbolic/utilities/mv.ml +++ b/src/symbolic/utilities/mv.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "mv" diff --git a/src/symbolic/utilities/mv.mli b/src/symbolic/utilities/mv.mli index adbabc34..c2f67e93 100644 --- a/src/symbolic/utilities/mv.mli +++ b/src/symbolic/utilities/mv.mli @@ -1,4 +1,5 @@ (** Symbolic execution of mv *) +open SymbolicUtility.Symbolic val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/rm.ml b/src/symbolic/utilities/rm.ml index 7f36f2c4..8ec80385 100644 --- a/src/symbolic/utilities/rm.ml +++ b/src/symbolic/utilities/rm.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "rm" diff --git a/src/symbolic/utilities/rm.mli b/src/symbolic/utilities/rm.mli index 29043abf..122237a7 100644 --- a/src/symbolic/utilities/rm.mli +++ b/src/symbolic/utilities/rm.mli @@ -1,2 +1,4 @@ +open SymbolicUtility.Symbolic + val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/test.ml b/src/symbolic/utilities/test.ml index 99896fd1..43a5985f 100644 --- a/src/symbolic/utilities/test.ml +++ b/src/symbolic/utilities/test.ml @@ -2,7 +2,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "test" diff --git a/src/symbolic/utilities/test.mli b/src/symbolic/utilities/test.mli index c93742c8..303e4061 100644 --- a/src/symbolic/utilities/test.mli +++ b/src/symbolic/utilities/test.mli @@ -1,7 +1,9 @@ +open SymbolicUtility.Symbolic + val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility module Bracket : sig val name : string - val interprete : SymbolicUtility.context -> SymbolicUtility.utility + val interprete : context -> utility end diff --git a/src/symbolic/utilities/touch.ml b/src/symbolic/utilities/touch.ml index efd9cfc2..31cb56da 100644 --- a/src/symbolic/utilities/touch.ml +++ b/src/symbolic/utilities/touch.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "touch" diff --git a/src/symbolic/utilities/touch.mli b/src/symbolic/utilities/touch.mli index 29043abf..122237a7 100644 --- a/src/symbolic/utilities/touch.mli +++ b/src/symbolic/utilities/touch.mli @@ -1,2 +1,4 @@ +open SymbolicUtility.Symbolic + val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/updateAlternatives.ml b/src/symbolic/utilities/updateAlternatives.ml index 53c0e82a..e2999537 100644 --- a/src/symbolic/utilities/updateAlternatives.ml +++ b/src/symbolic/utilities/updateAlternatives.ml @@ -1,4 +1,4 @@ -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "update-alternatives" diff --git a/src/symbolic/utilities/updateMenus.ml b/src/symbolic/utilities/updateMenus.ml index 84743e7a..000a978e 100644 --- a/src/symbolic/utilities/updateMenus.ml +++ b/src/symbolic/utilities/updateMenus.ml @@ -1,4 +1,4 @@ -open SymbolicUtility +open SymbolicUtility.Symbolic let name = "update-menus" diff --git a/src/symbolic/utilities/updateMenus.mli b/src/symbolic/utilities/updateMenus.mli index 29043abf..122237a7 100644 --- a/src/symbolic/utilities/updateMenus.mli +++ b/src/symbolic/utilities/updateMenus.mli @@ -1,2 +1,4 @@ +open SymbolicUtility.Symbolic + val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility diff --git a/src/symbolic/utilities/which.ml b/src/symbolic/utilities/which.ml index 8bf40bff..5015e7ac 100644 --- a/src/symbolic/utilities/which.ml +++ b/src/symbolic/utilities/which.ml @@ -1,7 +1,7 @@ open Format open Colis_constraints open Clause -open SymbolicUtility +open SymbolicUtility.Symbolic open Semantics__Result open Semantics__Buffers diff --git a/src/symbolic/utilities/which.mli b/src/symbolic/utilities/which.mli index 09391ded..bb0514f9 100644 --- a/src/symbolic/utilities/which.mli +++ b/src/symbolic/utilities/which.mli @@ -1,7 +1,9 @@ +open SymbolicUtility.Symbolic + val name : string -val interprete : SymbolicUtility.context -> SymbolicUtility.utility +val interprete : context -> utility module Silent : sig val name : string - val interprete : SymbolicUtility.context -> SymbolicUtility.utility + val interprete : context -> utility end From 21ec2e563cf0b47b01cf13cc8e8525aa7c4b91f3 Mon Sep 17 00:00:00 2001 From: Benedikt Becker Date: Thu, 20 Feb 2020 12:36:35 +0100 Subject: [PATCH 3/4] Organize and document interface of SymbolicUtility --- src/colis.ml | 5 +- src/colis.mli | 1 - src/symbolic/symbolicUtility.ml | 36 ++++++---- src/symbolic/symbolicUtility.mli | 115 +++++++++++++++++-------------- 4 files changed, 87 insertions(+), 70 deletions(-) diff --git a/src/colis.ml b/src/colis.ml index 7005ff7e..3676bda0 100644 --- a/src/colis.ml +++ b/src/colis.ml @@ -33,7 +33,6 @@ module Concrete = struct end module Symbolic = struct - module Filesystem = SymbolicUtility.SymbolicFilesystem include SymbolicUtility.Symbolic module FilesystemSpec = FilesystemSpec @@ -69,7 +68,7 @@ module Symbolic = struct let to_state ~prune_init_state ~root clause : Semantics.state = let root0 = if prune_init_state then None else Some root in - let filesystem = {Filesystem.root; clause; root0} in + let filesystem = {root; clause; root0} in {Semantics.filesystem; stdin=Common.Stdin.empty; stdout=Common.Stdout.empty; log=Common.Stdout.empty} let to_symbolic_state ~vars ~arguments state = @@ -198,7 +197,7 @@ type symbolic_config = { let print_symbolic_filesystem fmt fs = let open Colis_constraints in - let open Symbolic.Filesystem in + let open Symbolic in fprintf fmt "root: %a@\n" Var.pp fs.root; fprintf fmt "clause: %a@\n" Clause.pp_sat_conj fs.clause diff --git a/src/colis.mli b/src/colis.mli index ab53f77a..cb00d6f2 100644 --- a/src/colis.mli +++ b/src/colis.mli @@ -33,7 +33,6 @@ module Concrete : sig end module Symbolic : sig - module Filesystem = SymbolicUtility.SymbolicFilesystem include module type of SymbolicUtility.Symbolic module FilesystemSpec = FilesystemSpec diff --git a/src/symbolic/symbolicUtility.ml b/src/symbolic/symbolicUtility.ml index 03c366c0..1509df97 100644 --- a/src/symbolic/symbolicUtility.ml +++ b/src/symbolic/symbolicUtility.ml @@ -7,10 +7,8 @@ module type FILESYSTEM = sig end module type CASESPEC = sig - type case_spec val noop : case_spec - type filesystem val apply_spec : filesystem -> case_spec -> filesystem list end @@ -287,25 +285,22 @@ module Make (Filesystem: FILESYSTEM) = struct end end -module SymbolicFilesystem = struct - type filesystem = { - root: Var.t; - clause: Clause.sat_conj; - root0: Var.t option; - } -end +type symbolic_filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; +} -module SymbolicCaseSpec = struct +module SymbolicImplementation = struct type case_spec = Var.t -> Var.t -> Clause.t let noop = Clause.eq - type filesystem = SymbolicFilesystem.filesystem + type filesystem = symbolic_filesystem (** Apply the case specifications to a filesystem, resulting in a list of possible filesystems. *) let apply_spec fs spec = - let open SymbolicFilesystem in let root_is_root0 = (* fs.root0 = Some fs.root - garbare-collect fs.root only otherwise *) match fs.root0 with @@ -325,14 +320,25 @@ module SymbolicCaseSpec = struct end module Symbolic = struct - include Make (SymbolicFilesystem) - include MakeSpecifications (SymbolicCaseSpec) + + include Make (SymbolicImplementation) + + include MakeSpecifications (SymbolicImplementation) + type context = Semantics.utility_context = { cwd: Colis_constraints.Path.normal; env: string Env.SMap.t; args: string list; } - let noop = SymbolicCaseSpec.noop + + type filesystem = symbolic_filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; + } + + let noop = SymbolicImplementation.noop + let last_comp_as_hint: root:Var.t -> Path.t -> string option = fun ~root path -> match Path.split_last path with diff --git a/src/symbolic/symbolicUtility.mli b/src/symbolic/symbolicUtility.mli index 4bbff5fc..dfc4f55e 100644 --- a/src/symbolic/symbolicUtility.mli +++ b/src/symbolic/symbolicUtility.mli @@ -1,11 +1,17 @@ open Colis_constraints -open Semantics__Result +open Syntax__Syntax open Semantics__Buffers +open Semantics__Context +open Semantics__Input +open Semantics__Result +(** Definition of a symbolic filesystem to instantiate the symbolic interpreter *) module type FILESYSTEM = sig type filesystem end +(** Definition of a case specification and its application to a filesystem to define + utilities by specifications. *) module type CASESPEC = sig (** A case specification is a function from the old root variable and the new root root @@ -17,25 +23,29 @@ module type CASESPEC = sig val noop : case_spec type filesystem + + (** Apply a case specification to a filesystem, resulting in possible multiple new + filesystems *) val apply_spec : filesystem -> case_spec -> filesystem list end module Make (Filesystem: FILESYSTEM) : sig module Semantics : module type of SymbolicInterpreter__Interpreter.MakeSemantics (Filesystem) + open Semantics + (** {1 Interpretation of a colis program} *) + type sym_state = { - context : Semantics__Context.context; - state : Semantics.state; + context : context; + state : state; } - val interp_program : - Colis__Semantics__Input.input -> - sym_state list -> - Colis__Syntax__Syntax.program -> - Semantics.state list * Semantics.state list * Semantics.state list - + (** [interp_program inp stas p] symbolic interpretation configured by [inp] of a program + [p] in a list of symbolic input states [stas]. Returns the symbolic states + representing successful, errorneous, and incomplete interpretation. *) + val interp_program : input -> sym_state list -> program -> state list * state list * state list (** {1 Basics of symbolic utility} *) @@ -43,6 +53,8 @@ module Make (Filesystem: FILESYSTEM) : sig with boolean results *) type utility = state -> (state * bool result) list + (** {1 Registration and dispatch of utilities} *) + (** A symbolic utility is comprised of a name and a function that interpretes a [utility] in a [context]. *) module type SYMBOLIC_UTILITY = sig @@ -50,19 +62,34 @@ module Make (Filesystem: FILESYSTEM) : sig val interprete : utility_context -> utility end - (** {1 Registration} *) - (** Register a symbolic utility *) val register : (module SYMBOLIC_UTILITY) -> unit val is_registered : string -> bool - (** {1 Dispatch} *) - (** A saner way to call [dispatch] (e.g. in the implementation of utilities) *) val call : string -> utility_context -> string list -> utility - (** {1 Combinators} *) + (** {1 Basic utilities} *) + + (** Error utility *) + val error : utility:string -> string -> utility + + (** Unsupported stuff in a known utility. *) + val incomplete : utility:string -> string -> utility + + (* Unknown-unknown behaviour *) + val unknown : utility:string -> string -> utility + + (** {2 Printing} *) + + (** Print to stdout and log *) + val print_stdout : newline:bool -> string -> state -> state + + (** Print message as utility trace to log if it is not empty (marked as [UTL]) *) + val print_utility_trace : string -> state -> state + + (** {1 Utility combinators} *) (** [choice u1 u2] yields the utility that non-deterministacillay behaves like [u1] or [u2]. *) @@ -104,26 +131,7 @@ module Make (Filesystem: FILESYSTEM) : sig fails and [u2] is not executed *) val compose_strict : utility -> utility -> utility - (** {1 Auxiliaries} *) - - (** Error utility *) - val error : utility:string -> string -> utility - - (** Unsupported stuff in a known utility. *) - val incomplete : utility:string -> string -> utility - - (* Unknown-unknown behaviour *) - val unknown : utility:string -> string -> utility - - (** {2 Printing} *) - - (** Print to stdout and log *) - val print_stdout : newline:bool -> string -> state -> state - - (** Print message as utility trace to log if it is not empty (marked as [UTL]) *) - val print_utility_trace : string -> state -> state - - (** {2 Arguments Parsing} *) + (** {1 Arguments Parsing} *) val cmdliner_eval_utility : utility:string -> @@ -138,7 +146,7 @@ module Make (Filesystem: FILESYSTEM) : sig module MakeSpecifications (CaseSpec: CASESPEC with type filesystem = Filesystem.filesystem) : sig - (** A case in the specification is either a success or an error *) + (** A case in the specification is either a success, an error, or incomplete *) type case (** A success case (aka. return 0) *) @@ -156,32 +164,37 @@ module Make (Filesystem: FILESYSTEM) : sig end end -module SymbolicFilesystem : sig - type filesystem = { - root: Var.t; - clause: Clause.sat_conj; - root0: Var.t option; - } -end +type symbolic_filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; +} -module SymbolicCaseSpec : CASESPEC with - type filesystem = SymbolicFilesystem.filesystem and - type case_spec = Var.t -> Var.t -> Clause.t +(** Parameterization of the symbolic engine for constraint-based filesystem + [symbolic_filesystem] *) +module SymbolicImplementation : CASESPEC + with type filesystem = symbolic_filesystem + and type case_spec = Var.t -> Var.t -> Clause.t -(* Compatibility with previous SymbolicUtility *) +(* Compatibility with module SymbolicUtility before functorization of the symbolic engine *) module Symbolic : sig - include module type of Make (SymbolicFilesystem) - - include module type of MakeSpecifications (SymbolicCaseSpec) + include module type of Make (SymbolicImplementation) + include module type of MakeSpecifications (SymbolicImplementation) type context = Semantics.utility_context = { - cwd: Colis_constraints.Path.normal; + cwd: Path.normal; env: string Env.SMap.t; args: string list; } - val noop : SymbolicCaseSpec.case_spec + type filesystem = symbolic_filesystem = { + root: Var.t; + clause: Clause.sat_conj; + root0: Var.t option; + } + + val noop : SymbolicImplementation.case_spec (** Get the name of the last path component, if any, or of the hint root variable otherwise. The result is useful as a hint for From c4497c17b04fafba628665f00ff30ed6e0efe1a8 Mon Sep 17 00:00:00 2001 From: Benedikt Becker Date: Thu, 20 Feb 2020 14:13:31 +0100 Subject: [PATCH 4/4] Update proofs!! --- src/concrete/interpreter.mlw | 3 +- src/concrete/interpreter/why3session.xml | 494 ++--- src/concrete/interpreter/why3shapes.gz | Bin 11385 -> 11375 bytes src/concrete/semantics/why3session.xml | 20 +- src/concrete/semantics/why3shapes.gz | Bin 4324 -> 4316 bytes src/symbolic/symbolicInterpreter.mlw | 3 +- .../symbolicInterpreter/why3session.xml | 1898 +++++++++-------- .../symbolicInterpreter/why3shapes.gz | Bin 61860 -> 65740 bytes 8 files changed, 1220 insertions(+), 1198 deletions(-) diff --git a/src/concrete/interpreter.mlw b/src/concrete/interpreter.mlw index d2d518f2..3e6dcea7 100644 --- a/src/concrete/interpreter.mlw +++ b/src/concrete/interpreter.mlw @@ -14,7 +14,8 @@ module Semantics (* Instantiate the concrete semantics with our dummy filesystem *) clone export semantics.Semantics with - type filesystem = unit + type filesystem = unit, + axiom interp_utility_extends_output end module State diff --git a/src/concrete/interpreter/why3session.xml b/src/concrete/interpreter/why3session.xml index 2f91eae1..9c11ac70 100644 --- a/src/concrete/interpreter/why3session.xml +++ b/src/concrete/interpreter/why3session.xml @@ -14,24 +14,24 @@ - + - + - - + + - - + + - - + + - - + + - - + + @@ -41,566 +41,566 @@ - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -608,7 +608,7 @@ - + @@ -619,99 +619,99 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -722,61 +722,61 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -787,7 +787,7 @@ - + @@ -797,46 +797,46 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -846,40 +846,40 @@ - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/src/concrete/interpreter/why3shapes.gz b/src/concrete/interpreter/why3shapes.gz index 6182b91c9cc279b26057d6abe85a63c43aaf17ee..c959bad5f8c3d88db6143a58332fea237232870c 100644 GIT binary patch literal 11375 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
    SsozTe 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 diff --git a/src/concrete/semantics/why3session.xml b/src/concrete/semantics/why3session.xml index 3504f4ca..a7f18a9a 100644 --- a/src/concrete/semantics/why3session.xml +++ b/src/concrete/semantics/why3session.xml @@ -169,13 +169,15 @@ - + - + - + + + @@ -186,7 +188,7 @@ - + @@ -195,15 +197,15 @@ - + - + - + @@ -213,10 +215,10 @@ - + - + diff --git a/src/concrete/semantics/why3shapes.gz b/src/concrete/semantics/why3shapes.gz index 6ca41a738efc1cb8d3a1e4ac0020302cb62bca45..ce7529580e5bd2c34816318f1d49e6ee62448e82 100644 GIT binary patch literal 4316 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 - - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + @@ -44,44 +75,44 @@ - + - + - + - + - + - + - + - + - + - + @@ -93,1229 +124,1207 @@ - + - + - + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - - - - - - - - + - - - - - - - - - - - - - - - - + - + - + - + - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + - + - + - + - - - - - - - - + - - - - - - - - + - + - + - + + + + + - + - + - + - + - + - + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + - + - + - + - + - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1324,7 +1333,7 @@ - + @@ -1335,1034 +1344,1041 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2370,126 +2386,137 @@ - + - + + + + + + + + - + - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + - + - + - + - + - + + + + + - + - + - + - + - + @@ -2497,25 +2524,41 @@ - - + + + + + + + + + + + + + + + + + + - + - + - + - + - + @@ -2532,79 +2575,69 @@ - + - + - + + + + + - + - + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + @@ -2612,64 +2645,49 @@ - + - - - - - + - + - + - + - + - + - + - - - - - + - + - + - - + + - - - - - - - - - + + diff --git a/src/symbolic/symbolicInterpreter/why3shapes.gz b/src/symbolic/symbolicInterpreter/why3shapes.gz index 8333f1951affc9b25e542c22571afea66987943e..55bd1abb4d344c0a432bb13413c1017245833d0f 100644 GIT binary patch literal 65740 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�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