diff --git a/Makefile b/Makefile index fc57451b0098a4e6a322ba70a38a37da3ba5a4a5..228828bb6112600704996a2ce46dea573c3d90eb 100644 --- a/Makefile +++ b/Makefile @@ -925,6 +925,19 @@ PLUGIN_TYPES_TODOC:=$(addsuffix .mli,$(VALUE_TYPES)) $(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME))) +######### +# Reduc # +######### +PLUGIN_ENABLE:=$(ENABLE_REDUC) +PLUGIN_DYNAMIC:=$(DYNAMIC_REDUC) +PLUGIN_NAME:=Reduc +PLUGIN_DISTRIBUTED:=yes +PLUGIN_DIR:=src/plugins/reduc +PLUGIN_CMO:=reduc_options misc value2acsl collect hyp register +PLUGIN_DEPENDENCIES:=Eva + +$(eval $(call include_generic_plugin_Makefile,$(PLUGIN_NAME))) + ################## # Occurrence # ################## diff --git a/headers/header_spec.txt b/headers/header_spec.txt index c62c5c099a76660fec06a6564f5e8d89e710add8..1ba479e563c1051806697fb923ff0dd0f39c15d5 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1119,6 +1119,18 @@ src/plugins/qed/pretty.ml: CEA_WP src/plugins/qed/pretty.mli: CEA_WP src/plugins/qed/term.ml: CEA_WP src/plugins/qed/term.mli: CEA_WP +src/plugins/reduc/Reduc.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/collect.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/collect.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/hyp.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/hyp.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/misc.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/misc.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/reduc_options.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/reduc_options.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/register.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/value2acsl.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/reduc/value2acsl.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/report/.gitignore: .ignore src/plugins/report/Makefile.in: CEA_LGPL_OR_PROPRIETARY src/plugins/report/Report.mli: CEA_LGPL_OR_PROPRIETARY @@ -1579,6 +1591,8 @@ src/plugins/wp/Matrix.ml: CEA_WP src/plugins/wp/Matrix.mli: CEA_WP src/plugins/wp/MemoryContext.ml: CEA_WP src/plugins/wp/MemoryContext.mli: CEA_WP +src/plugins/wp/MemDebug.ml: CEA_WP +src/plugins/wp/MemDebug.mli: CEA_WP src/plugins/wp/MemEmpty.ml: CEA_WP src/plugins/wp/MemEmpty.mli: CEA_WP src/plugins/wp/MemLoader.ml: CEA_WP @@ -1589,6 +1603,8 @@ src/plugins/wp/MemRegion.ml: CEA_WP src/plugins/wp/MemRegion.mli: CEA_WP src/plugins/wp/MemTyped.ml: CEA_WP src/plugins/wp/MemTyped.mli: CEA_WP +src/plugins/wp/MemVal.ml: CEA_WP +src/plugins/wp/MemVal.mli: CEA_WP src/plugins/wp/MemVar.ml: CEA_WP src/plugins/wp/MemVar.mli: CEA_WP src/plugins/wp/MemZeroAlias.ml: CEA_WP diff --git a/src/plugins/reduc/Reduc.mli b/src/plugins/reduc/Reduc.mli new file mode 100644 index 0000000000000000000000000000000000000000..cce51dc83ef23f10bb7ed95d5a93dfc632b91896 --- /dev/null +++ b/src/plugins/reduc/Reduc.mli @@ -0,0 +1,21 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) diff --git a/src/plugins/reduc/collect.ml b/src/plugins/reduc/collect.ml new file mode 100644 index 0000000000000000000000000000000000000000..427d97ecb56435975f7d248801908446f790ac7a --- /dev/null +++ b/src/plugins/reduc/collect.ml @@ -0,0 +1,206 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Cil_datatype + +module Options = Reduc_options + +exception Alarm_reached + +let dkey = Options.register_category "collect" +let debug = Options.debug ~dkey + +type 'a alarm_component = Emitter.t -> + kernel_function -> + stmt -> + rank:int -> Alarms.alarm -> code_annotation -> 'a -> 'a + +type varh = VarAll +type annoth = AnnotAll | AnnotInout + +type env = { + rel_varh: varh; + rel_annoth: annoth; + rel_stmts: Stmt.Set.t; + rel_vars: LvalStructEq.Set.t Stmt.Map.t; +} + +let empty_env varh annoth = { + rel_varh = varh; + rel_annoth = annoth; + rel_stmts = Stmt.Set.empty; + rel_vars = Stmt.Map.empty; +} + +let env_with_stmt env stmt = + { env with rel_stmts = Stmt.Set.add stmt env.rel_stmts } + +let env_with_stmt_vars env stmt lvs = + let vars = + try + Stmt.Map.find stmt env.rel_vars + with Not_found -> LvalStructEq.Set.empty in + let new_vars = LvalStructEq.Set.(union vars (of_list lvs)) in + { env with rel_vars = Stmt.Map.add stmt new_vars env.rel_vars } + +(* ************************************************************************ *) +(** Alarms to relevant locations *) +(* ************************************************************************ *) + +class stmts_vis + (alarm_stmt : stmt) + (env: env ref) = object(_) + inherit Cil.nopCilVisitor + + method! vstmt stmt = + env := env_with_stmt !env stmt; + if Stmt.equal stmt alarm_stmt then begin + raise Alarm_reached + end; + Cil.DoChildren +end + +let rec collect_off typ = + match typ with + | TInt _ | TFloat _ -> [ NoOffset ] + | TComp ({cfields = Some flds}, _, _) -> + List.fold_left collect_fields [] flds + | TArray (arrtyp, e_opt, _, _) -> + debug "Array of length %a" (Pretty_utils.pp_opt Printer.pp_exp) e_opt; + begin try collect_array arrtyp [] (Cil.lenOfArray64 e_opt) + with Cil.LenOfArray -> [] end + | TVoid _ | TFun _ | TPtr _ | TEnum _ | TNamed _ | TBuiltin_va_list _ + | TComp ({cfields = None}, _, _)-> [] + +and collect_fields acc fld = + let offs = collect_off fld.ftype in + List.map (fun off -> Field (fld, off)) offs @ acc + +and collect_array typ acc = function + | x when Integer.is_zero x -> acc + | x -> + let offs = collect_off typ in + let exp = Cil.kinteger64 ~loc:Location.unknown x in + let acc' = acc @ List.map (fun off -> Index(exp ,off)) offs in + collect_array typ acc' (Integer.add x Integer.minus_one) + +let collect_varinfo acc x = + let offs = collect_off x.vtype in + List.map (fun off -> (Var x, off)) offs @ acc + +class inout_vis + (alarm_stmt : stmt) + (kf : Kernel_function.t) + (env : env ref) = object(self) + inherit stmts_vis alarm_stmt env as super + + val mutable vars = [] + + method !vfunc _ = + vars <- Kernel_function.((get_locals kf) @ (get_formals kf)); + Cil.DoChildren + + method private is_first_stmt stmt = + try Stmt.equal stmt (Kernel_function.find_first_stmt kf) + with Kernel_function.No_Statement -> assert false + + method !vstmt stmt = + let out = !Db.Outputs.statement stmt in + let filter_out vi = + let open Locations in + let zone = enumerate_bits (loc_of_varinfo vi) in + Zone.intersects out zone + in + let effect = + if self#is_first_stmt stmt then vars + else List.filter filter_out vars + in + let vars = List.fold_left collect_varinfo [] effect in + env := env_with_stmt_vars !env stmt vars; + super#vstmt stmt +end + +let get_relevant_stmts kf stmt env = + let do_visit ref_env = match !ref_env.rel_annoth with + | AnnotAll -> Cil.visitCilFunction (new stmts_vis stmt ref_env) + | AnnotInout -> + Cil.visitCilFunction (new inout_vis stmt kf ref_env :> Cil.cilVisitor) + in + let renv = ref env in + try + let fundec = Kernel_function.get_definition kf in + ignore (do_visit renv fundec); + !renv + with Alarm_reached -> + !renv + +let get_relevant _emit kf stmt ~rank:_ _a _annot env = + get_relevant_stmts kf stmt env + +let should_annotate_stmt env stmt = + Stmt.Set.mem stmt env.rel_stmts + +(* ************************************************************************ *) +(** Relevant locations to relevant variables *) +(* ************************************************************************ *) + +class relevant_stmt_vars_visitor + (vars: LvalStructEq.Set.t ref) = object(_) + inherit Cil.nopCilVisitor + + method! vstmt stmt = + match stmt.skind with + | Goto _ | UnspecifiedSequence _ -> Cil.SkipChildren + | _ -> Cil.DoChildren + + method! vblock _ = Cil.SkipChildren + + method !vexpr e = + begin match e.enode with + | Lval lv | AddrOf lv | StartOf lv -> + vars := LvalStructEq.Set.add lv !vars + | _ -> () + end; + Cil.DoChildren +end + +let get_relevant_all_vars_stmt kf stmt = + let do_visit ref_vars = + Cil.visitCilStmt (new relevant_stmt_vars_visitor ref_vars) + in + let ref_vars = ref LvalStructEq.Set.empty in + ignore (do_visit ref_vars stmt); + let locals = Kernel_function.get_locals kf in + let formals = Kernel_function.get_formals kf in + let vars = List.map Cil.var (locals @ formals) in + vars @ LvalStructEq.Set.elements !ref_vars + +let get_relevant_inout_vars_stmt env stmt = + try + let vars = Stmt.Map.find stmt env.rel_vars in + LvalStructEq.Set.elements vars + with Not_found -> assert false + +let get_relevant_vars_stmt env kf stmt = match env.rel_annoth with + | AnnotAll -> get_relevant_all_vars_stmt kf stmt + | AnnotInout -> get_relevant_inout_vars_stmt env stmt diff --git a/src/plugins/reduc/collect.mli b/src/plugins/reduc/collect.mli new file mode 100644 index 0000000000000000000000000000000000000000..bfbf024ec5622880024e6bd13d4f1f6021953aab --- /dev/null +++ b/src/plugins/reduc/collect.mli @@ -0,0 +1,40 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +type 'a alarm_component = Emitter.t -> + kernel_function -> + stmt -> + rank:int -> Alarms.alarm -> code_annotation -> 'a -> 'a + +type env + +type varh = VarAll +type annoth = AnnotAll | AnnotInout + +val empty_env: varh -> annoth -> env + +val get_relevant: env alarm_component (* Set(loc) * Set(exp) ? *) + +val should_annotate_stmt: env -> stmt -> bool +val get_relevant_vars_stmt: env -> kernel_function -> stmt -> lval list diff --git a/src/plugins/reduc/hyp.ml b/src/plugins/reduc/hyp.ml new file mode 100644 index 0000000000000000000000000000000000000000..fc4fabf2763c7db2b26efafaf8ff26481de7d763 --- /dev/null +++ b/src/plugins/reduc/hyp.ml @@ -0,0 +1,54 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +let pred_opt_from_expr_state state e = + try + Value2acsl.lval_to_predicate state e + with + | Cvalue.V.Not_based_on_null -> + Misc.not_implemented ~what:"Value not based on null"; + None + | Misc.Not_implemented what -> + Misc.not_implemented ~what; + None + +class hypotheses_visitor (env: Collect.env) = object(self) + inherit Visitor.generic_frama_c_visitor (Visitor_behavior.inplace ()) + + method! vstmt_aux stmt = + let kf = Option.get (self#current_kf) in + let state = Db.Value.get_stmt_state stmt in + if Collect.should_annotate_stmt env stmt then begin + let vars = Collect.get_relevant_vars_stmt env kf stmt in + List.iter + (fun e -> + let p_opt = pred_opt_from_expr_state state e in + Option.iter (Misc.assert_and_validate ~kf stmt) p_opt) + vars + end; + Cil.DoChildren +end + + +let generate_hypotheses env = + let visitor = new hypotheses_visitor env in + Cil.visitCilFileSameGlobals (visitor :> Cil.cilVisitor) (Ast.get ()) diff --git a/src/plugins/reduc/hyp.mli b/src/plugins/reduc/hyp.mli new file mode 100644 index 0000000000000000000000000000000000000000..7f19f530cd30f6008af7aad8aa08992adf70b246 --- /dev/null +++ b/src/plugins/reduc/hyp.mli @@ -0,0 +1,23 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +val generate_hypotheses: Collect.env -> unit diff --git a/src/plugins/reduc/misc.ml b/src/plugins/reduc/misc.ml new file mode 100644 index 0000000000000000000000000000000000000000..1e5fda8a19eb7cb3465560d5b045e573068d7f4e --- /dev/null +++ b/src/plugins/reduc/misc.ml @@ -0,0 +1,53 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +module Options = Reduc_options + + +exception Not_implemented of string + +let not_implemented ~what = + Options.warning "Not implemented: `%s'. Ignoring." what + + +let emitter = + Emitter.create + "Reduc" + [ Emitter.Code_annot; Emitter.Property_status ] + ~correctness:[] + ~tuning:[] + +(* ******************************************************) +(* Annotations and function contracts helpers *) +(* ******************************************************) +let validate_ip ip = + Property_status.emit emitter ~hyps:[] ip Property_status.True + +let assert_and_validate ~kf stmt p = + let p = { tp_kind = Assert ; tp_statement = p } in + let annot = Logic_const.new_code_annotation (AAssert([], p)) in + Annotations.add_code_annot emitter ~kf stmt annot ; + List.iter + validate_ip + (Property.ip_of_code_annot kf stmt annot) diff --git a/src/plugins/reduc/misc.mli b/src/plugins/reduc/misc.mli new file mode 100644 index 0000000000000000000000000000000000000000..6bf9d91a6d7d8c3c4f18dd939411df059c24d1b4 --- /dev/null +++ b/src/plugins/reduc/misc.mli @@ -0,0 +1,34 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +exception Not_implemented of string +val not_implemented: what:string -> unit + +(* [emitter] of the reduc plugin. *) +val emitter: Emitter.t + +(* ******************************************************) +(* Annotations and function contracts helpers *) +(* ******************************************************) +val assert_and_validate: kf:Kernel_function.t -> stmt -> predicate -> unit diff --git a/src/plugins/reduc/reduc_options.ml b/src/plugins/reduc/reduc_options.ml new file mode 100644 index 0000000000000000000000000000000000000000..3a5910a067fcc4f274525d17f72f889457fc8db4 --- /dev/null +++ b/src/plugins/reduc/reduc_options.ml @@ -0,0 +1,54 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include Plugin.Register + (struct + let name = "Reduction" + let shortname = "reduc" + let help = "Generate ACSL annotations from Value Analysis informations" + end) + +module Reduc = + Bool + (struct + let option_name = "-reduc" + let help = "Use reduc" + let default = false + end) + +module GenAnnot = + String + (struct + let option_name = "-reduc-gen-annot" + let arg_name = "gen-annot-heuristic" + let help = "Heuristic to generate annotations from Value" + let default = "inout" + end) + +module GenVars = + String + (struct + let option_name = "-reduc-gen-vars" + let arg_name = "gen-vars-heuristic" + let help = "Heuristic to generate annotations for variables" + let default = "all" + end) diff --git a/src/plugins/reduc/reduc_options.mli b/src/plugins/reduc/reduc_options.mli new file mode 100644 index 0000000000000000000000000000000000000000..5eeb81c7c625e0d4e0b958149aad6ed6f64b62e8 --- /dev/null +++ b/src/plugins/reduc/reduc_options.mli @@ -0,0 +1,28 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +include Plugin.General_services + +module Reduc: Parameter_sig.Bool + +module GenAnnot: Parameter_sig.String +module GenVars: Parameter_sig.String diff --git a/src/plugins/reduc/register.ml b/src/plugins/reduc/register.ml new file mode 100644 index 0000000000000000000000000000000000000000..0274d1458579037b452eaf4e8053ecb67610aa81 --- /dev/null +++ b/src/plugins/reduc/register.ml @@ -0,0 +1,44 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +module Options = Reduc_options + +let command_line () = + let varh = match Options.GenVars.get () with + | "all" -> Collect.VarAll + | _ -> Options.fatal "Not a valid variable heuristic" in + let annoth = match Options.GenAnnot.get () with + | "all" -> Collect.AnnotAll + | "inout" -> Collect.AnnotInout + | _ -> Options.fatal "Not a valid annotation heuristic" + in + varh, annoth + +let main () = + if (Options.Reduc.get ()) then begin + let varh, annoth = command_line () in + let env = Alarms.fold Collect.get_relevant (Collect.empty_env varh annoth) in + Hyp.generate_hypotheses env; + () + end + +let () = Db.Main.extend main diff --git a/src/plugins/reduc/value2acsl.ml b/src/plugins/reduc/value2acsl.ml new file mode 100644 index 0000000000000000000000000000000000000000..c91d1ba6992418ffebcea1371b2b587aa9d5ea36 --- /dev/null +++ b/src/plugins/reduc/value2acsl.ml @@ -0,0 +1,179 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_datatype +open Cil_types + +open Misc + +module AI = Abstract_interp + +module Options = Reduc_options + +let not_yet ~what = + Options.warning "Not yet: %s" what + +(* [int_to_predicate ~loc lop te i] create a predicate where [te] [lop] [i]. *) +let int_to_predicate ~loc ~lop te (i: Integer.t) = + let ti = Logic_const.tint i in + Logic_const.prel ~loc (lop, te, ti) + +(* [float_to_predicate ~loc lop te f] create a predicate where [te] [lop] [f]. *) +let float_to_predicate ~loc ~lop t f = + let tf = Logic_const.treal ~loc f in + Logic_const.prel ~loc (lop, t, tf) + +(* [interval_to_predicate_opt ~loc te min max] may create an interval + predicate where [min] <= [te] <= [max]. *) +let interval_to_predicate_opt ~loc te min max = + let min_predicate = + int_to_predicate ~loc ~lop:Rge te + and max_predicate = + int_to_predicate ~loc ~lop:Rle te + in + Extlib.merge_opt + (fun _ pmin pmax -> Logic_const.pand ~loc (pmin, pmax)) + () + (Option.map min_predicate min) + (Option.map max_predicate max) + +(* [congruence_to_predicate_opt ~loc te rem modulo] may create a congruence + predicate where [te] mod [modulo] equals [rem] *) +let congruence_to_predicate_opt ~loc te rem modulo = + if Integer.(equal modulo one) then None + else + let tmodulo = Logic_const.tint ~loc modulo in + let tbinop = TBinOp(Mod, te, tmodulo) in + (* since 0 <= rem < mod *) + let tmodop = Logic_const.term ~loc tbinop (Linteger) in + let p = int_to_predicate ~loc ~lop:Req tmodop rem in + Some p + +let fval_to_predicate ~loc t f = + match Ival.min_and_max_float f with + | None, false -> assert false + | _, true -> Logic_const.pfalse (* todo *) + | Some (fmn, fmx), _ -> + let mn = Fval.F.to_float fmn in + let mx = Fval.F.to_float fmx in + let pmn = float_to_predicate ~loc ~lop:Rge t mn in + let pmx = float_to_predicate ~loc ~lop:Rle t mx in + Logic_const.pand ~loc (pmn, pmx) + +(* [ival_to_predicate_opt ~loc t ival] may create a predicate from different + domains contained in [ival]. *) +let ival_to_predicate_opt ~loc t ival = + if Ival.is_float ival + then Some (fval_to_predicate ~loc t ival) + else + match Ival.project_small_set ival with + | Some is -> + let ps = List.map (int_to_predicate ~loc ~lop:Req t) is in + let pors = Logic_const.pors ps in + Some pors + | None -> + match Ival.min_max_r_mod ival with + | (None, None, _, modulo) when AI.Int.is_one modulo ->(*Top*) None + | (min, max, rem, modulo) -> + let pint = interval_to_predicate_opt ~loc t min max in + let pcon = congruence_to_predicate_opt ~loc t rem modulo in + Extlib.merge_opt + (fun _ pint pcon -> Logic_const.pand ~loc (pint, pcon)) + () + pint + pcon + + +(* [valid_to_predicate_opt ~loc t valid] may create validity predicate *only* + for valid locations. *) +let valid_to_predicate_opt ~loc t valid = + let here_lbl = BuiltinLabel Here in + match valid with + | Base.Empty -> None + | Base.Known (bmn, bmx) | Base.Unknown (bmn, Some bmx, _) -> + let tbmn = Cil.lconstant ~loc bmn in + let tbmx = Cil.lconstant ~loc bmx in + let p = Logic_const.pvalid_range ~loc (here_lbl, t, tbmn, tbmx) in + Some p + | Base.Unknown (_, _, _) -> None + | Base.Variable { Base.min_alloc = bmn } when Integer.(equal bmn minus_one) -> + not_yet "Invalid Base.Variable above max_alloc"; + None + | Base.Variable { Base.min_alloc = bmn; Base.max_alloc = bmx } -> + not_yet "Invalid Base.Variable above max_alloc"; + let tbmn = Cil.lconstant ~loc bmn in + let tbmx = Cil.lconstant ~loc bmx in + let p = Logic_const.pvalid_range ~loc (here_lbl, t, tbmn, tbmx) in + Some p + | Base.Invalid -> + not_yet "Invalid Base"; + None + +let base_to_predicate ~loc t (b: Base.t) = + match b with + | Base.Var (_vi, valid) | Base.Allocated (_vi, _, valid) -> + valid_to_predicate_opt ~loc t valid + | Base.CLogic_Var (_lvi, _typ, _) -> raise (Not_implemented "Base.CLogic_Var") + | Base.Null -> assert false (*by projecting ival*) + | Base.String _ -> raise (Not_implemented "Base.String") + + +let base_offset_to_predicate ~loc t b o = + if Ival.equal Ival.zero o then + base_to_predicate ~loc t b + else begin + not_yet ~what:"Base w/ offsets"; + None + end + +let _base_offsets_to_predicate ~loc t (m: Cvalue.V.M.t) = + let aux b o (acc: predicate list) = + let p_opt = base_offset_to_predicate ~loc t b o in + (Option.to_list p_opt) @ acc + in + match Cvalue.V.M.fold aux m [] with + | [] -> None + | ps -> Some (Logic_const.pands ps) + +let value_to_predicate_opt ?(loc=Location.unknown) t v = + let open Cvalue.V in + match v with + | Top _ -> None + | Map _m -> + try + let ival = project_ival v in + ival_to_predicate_opt ~loc t ival + with + | Not_based_on_null -> (* base_offsets_to_predicate ~loc t m *) None + +let exp_to_predicate ?(loc=Location.unknown) state e = + let value = !Db.Value.eval_expr ~with_alarms:CilE.warn_none_mode state e in + let te = Logic_utils.expr_to_term ~coerce:false e in + value_to_predicate_opt ~loc te value + +let lval_to_predicate ?(loc=Location.unknown) state lv = + let value = snd(!Db.Value.eval_lval + ~with_alarms:CilE.warn_none_mode None state lv) + in + let e = Cil.new_exp ~loc (Lval lv) in + let te = Logic_utils.expr_to_term ~coerce:false e in + value_to_predicate_opt ~loc te value diff --git a/src/plugins/reduc/value2acsl.mli b/src/plugins/reduc/value2acsl.mli new file mode 100644 index 0000000000000000000000000000000000000000..17546299c2191a9c2c5ea076b463904ccd48ed60 --- /dev/null +++ b/src/plugins/reduc/value2acsl.mli @@ -0,0 +1,31 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(* [value_to_predicate_opt loc t value] may create a predicate given a [value] + about some [term]. + @return None if no such predicate can be created. *) +val value_to_predicate_opt: ?loc:location -> term -> Cvalue.V.t -> predicate option + +val lval_to_predicate: ?loc:location -> Cvalue.Model.t -> lval -> predicate option +val exp_to_predicate: ?loc:location -> Cvalue.Model.t -> exp -> predicate option diff --git a/src/plugins/wp/Factory.ml b/src/plugins/wp/Factory.ml index 668610d8cfccda532eb16e76f332df7ef6875d5a..2661b7572e3ec5d2ce5155fbca313e9155c447ed 100644 --- a/src/plugins/wp/Factory.ml +++ b/src/plugins/wp/Factory.ml @@ -24,7 +24,7 @@ (* --- Model Factory --- *) (* -------------------------------------------------------------------------- *) -type mheap = Hoare | ZeroAlias | Region | Typed of MemTyped.pointer +type mheap = Hoare | ZeroAlias | Region | Typed of MemTyped.pointer | Eva type mvar = Raw | Var | Ref | Caveat type setup = { @@ -68,6 +68,7 @@ let descr_mheap d = function | ZeroAlias -> main d "zeroalias" | Hoare -> main d "hoare" | Typed p -> main d "typed" ; descr_mtyped d p + | Eva -> main d "eva" let descr_mvar d = function | Var -> () @@ -238,6 +239,8 @@ module Model_Typed_Var = Register(Var)(MemTyped) module Model_Typed_Ref = Register(Ref)(MemTyped) module Model_Caveat = Register(Caveat)(MemTyped) +module MemVal = MemVal.Make(MemVal.Eva) + module MakeCompiler(M:Sigs.Model) = struct module M = M module C = CodeSemantics.Make(M) @@ -253,6 +256,7 @@ module Comp_MemTyped = MakeCompiler(MemTyped) module Comp_Typed_Var = MakeCompiler(Model_Typed_Var) module Comp_Typed_Ref = MakeCompiler(Model_Typed_Ref) module Comp_Caveat = MakeCompiler(Model_Caveat) +module Comp_MemVal = MakeCompiler(MemVal) let compiler mheap mvar : (module Sigs.Compiler) = @@ -265,6 +269,7 @@ let compiler mheap mvar : (module Sigs.Compiler) = | Typed _ , Raw -> (module Comp_MemTyped) | Typed _ , Var -> (module Comp_Typed_Var) | Typed _ , Ref -> (module Comp_Typed_Ref) + | Eva, _ -> (module Comp_MemVal) (* -------------------------------------------------------------------------- *) (* --- Tuning --- *) @@ -274,6 +279,7 @@ let configure_mheap = function | Hoare -> MemEmpty.configure () | ZeroAlias -> MemZeroAlias.configure () | Region -> MemRegion.configure () + | Eva -> MemVal.configure () | Typed p -> let rollback_memtyped = MemTyped.configure () in let orig_memtyped_pointer = Context.push MemTyped.pointer p in @@ -343,6 +349,7 @@ let split ~warning (m:string) : string list = (fun c -> match c with | 'A' .. 'Z' -> Buffer.add_char buffer c + | '0' .. '9' -> Buffer.add_char buffer c | '_' | ',' | '@' | '+' | ' ' | '\t' | '\n' | '(' | ')' -> flush () | _ -> warning (Printf.sprintf "In model spec %S : unexpected character '%c'" m c) @@ -357,6 +364,7 @@ let update_config ~warning m s = function | "TYPED" -> { s with mheap = Typed MemTyped.Fits } | "CAST" -> { s with mheap = Typed MemTyped.Unsafe } | "NOCAST" -> { s with mheap = Typed MemTyped.NoCast } + | "EVA" -> { s with mheap = Eva } | "CAVEAT" -> { s with mvar = Caveat } | "RAW" -> { s with mvar = Raw } | "REF" -> { s with mvar = Ref } diff --git a/src/plugins/wp/Factory.mli b/src/plugins/wp/Factory.mli index 07df6d8730678c5a7341340e388aafc76db23630..9dbd3daf115a1e3e43a46896375f0b121227f388 100644 --- a/src/plugins/wp/Factory.mli +++ b/src/plugins/wp/Factory.mli @@ -24,7 +24,7 @@ (* --- Model Factory --- *) (* -------------------------------------------------------------------------- *) -type mheap = Hoare | ZeroAlias | Region | Typed of MemTyped.pointer +type mheap = Hoare | ZeroAlias | Region | Typed of MemTyped.pointer | Eva type mvar = Raw | Var | Ref | Caveat type setup = { diff --git a/src/plugins/wp/GuiPanel.ml b/src/plugins/wp/GuiPanel.ml index aebf56abb66ae105467c5a63c4667bcc9c9f9fed..8a0983e0ea936065024d4df7c5acb185158fc92e 100644 --- a/src/plugins/wp/GuiPanel.ml +++ b/src/plugins/wp/GuiPanel.ml @@ -128,7 +128,7 @@ let run_and_prove (* --- Model Panel --- *) (* ------------------------------------------------------------------------ *) -type memory = TREE | HOARE | TYPED | REGION +type memory = TREE | HOARE | TYPED | REGION | EVA class model_selector (main : Design.main_window_extension_points) = let dialog = new Wpane.dialog @@ -136,6 +136,7 @@ class model_selector (main : Design.main_window_extension_points) = let memory = new Widget.group HOARE in let r_hoare = memory#add_radio ~label:"Hoare Memory Model" ~value:HOARE () in let r_typed = memory#add_radio ~label:"Typed Memory Model" ~value:TYPED () in + let r_eva = memory#add_radio ~label:"Eva Memory Model" ~value:EVA () in let c_casts = new Widget.checkbox ~label:"Unsafe casts" () in let c_byref = new Widget.checkbox ~label:"Reference Arguments" () in let c_ctxt = new Widget.checkbox ~label:"Context Arguments (Caveat)" () in @@ -148,6 +149,7 @@ class model_selector (main : Design.main_window_extension_points) = begin dialog#add_row r_hoare#coerce ; dialog#add_row r_typed#coerce ; + dialog#add_row r_eva#coerce ; dialog#add_row c_casts#coerce ; dialog#add_row c_byref#coerce ; dialog#add_row c_ctxt#coerce ; @@ -174,7 +176,9 @@ class model_selector (main : Design.main_window_extension_points) = | ZeroAlias -> memory#set TREE | Region -> memory#set REGION | Hoare -> memory#set HOARE - | Typed m -> memory#set TYPED ; c_casts#set (m = MemTyped.Unsafe)) ; + | Typed m -> memory#set TYPED ; c_casts#set (m = MemTyped.Unsafe) + | Eva -> memory#set EVA + ) ; c_byref#set (s.mvar = Ref) ; c_ctxt#set (s.mvar = Caveat) ; c_cint#set (s.cint = Cint.Machine) ; @@ -188,6 +192,7 @@ class model_selector (main : Design.main_window_extension_points) = | HOARE -> Hoare | TYPED -> Typed (if c_casts#get then MemTyped.Unsafe else MemTyped.Fits) + | EVA -> Eva in { mheap = m ; mvar = if c_ctxt#get then Caveat else if c_byref#get then Ref else Var ; diff --git a/src/plugins/wp/Makefile.in b/src/plugins/wp/Makefile.in index ffba8070d60ca2f3a44b4fcd662f65f7047ee3f3..a4b18ac20739034825c65368744722c99554305f 100644 --- a/src/plugins/wp/Makefile.in +++ b/src/plugins/wp/Makefile.in @@ -79,9 +79,9 @@ PLUGIN_CMO:= \ CodeSemantics \ LogicCompiler \ LogicSemantics LogicAssigns \ - Sigma MemLoader \ + Sigma MemLoader MemDebug \ MemEmpty MemZeroAlias MemVar \ - MemMemory MemTyped MemRegion \ + MemMemory MemTyped MemRegion MemVal \ wpReached wpRTE wpAnnot wpTarget \ CfgCompiler StmtSemantics \ VCS script proof wpo wpReport \ @@ -246,7 +246,7 @@ WP_API_BASE= \ Plang.mli Pcfg.mli Pcond.mli \ CodeSemantics.mli \ LogicCompiler.mli LogicSemantics.mli \ - Sigma.mli MemVar.mli MemTyped.mli \ + Sigma.mli MemVar.mli MemTyped.mli MemVal.mli \ CfgCompiler.mli StmtSemantics.mli \ Factory.mli driver.mli VCS.mli Tactical.mli Strategy.mli Auto.mli \ VC.mli wpo.mli ProverTask.mli prover.mli diff --git a/src/plugins/wp/MemDebug.ml b/src/plugins/wp/MemDebug.ml new file mode 100644 index 0000000000000000000000000000000000000000..3bbb3ce1476182a5820cf93b52e51fffb1582d48 --- /dev/null +++ b/src/plugins/wp/MemDebug.ml @@ -0,0 +1,279 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_datatype +open Lang.F +open Sigs + +(* ------------------------------------------------------------------------ *) +(* ---- Pretty-printers ---- *) +(* ------------------------------------------------------------------------ *) + +let pp_sequence pp_val fmt seq = + Format.fprintf fmt "@[{pre=%a,@,post=%a}@]" + pp_val seq.pre pp_val seq.post + +let pp_equation fmt = function + | Set (lt, rt) -> Format.fprintf fmt "@[%a =@, %a@]" pp_term lt pp_term rt + | Assert pred -> pp_pred fmt pred + +let pp_acs fmt = function + | RW -> Format.pp_print_string fmt "RW" + | RD -> Format.pp_print_string fmt "RD" + | OBJ -> Format.pp_print_string fmt "OBJ" + +let pp_value pp_loc fmt = function + | Val t -> pp_term fmt t + | Loc l -> pp_loc fmt l + +let pp_rloc pp_loc fmt = function + | Rloc(_obj,l) -> Format.fprintf fmt "[@{%a}@]" pp_loc l + | Rrange(l,_obj,tmin,tmax) -> + Format.fprintf fmt "@[%a+(%a..%a)@]" pp_loc l + (Pretty_utils.pp_opt pp_term) tmin (Pretty_utils.pp_opt pp_term) tmax + +let pp_sloc pp_loc fmt = function + | Sloc l -> Format.fprintf fmt "@[{%a}@]" pp_loc l + | Sarray(l,_obj,size) -> Format.fprintf fmt "@[%a+(0..%d)@]" pp_loc l size + | Srange(l,_obj,tmin,tmax) -> + Format.fprintf fmt "@[%a+(%a..%a)@]" pp_loc l + (Pretty_utils.pp_opt pp_term) tmin (Pretty_utils.pp_opt pp_term) tmax + | Sdescr(xs,l,p) -> + Format.fprintf fmt "@[{ %a @,| %a@,; %a }@]" pp_loc l + (Pretty_utils.pp_list pp_var) xs pp_pred p + +(* ------------------------------------------------------------------------ *) +(* ---- Debug Memory Model ---- *) +(* ------------------------------------------------------------------------ *) + +let dkey_cons = Wp_parameters.register_category "memdebug:cons" +let dkey_loc = Wp_parameters.register_category "memdebug:loc" +let dkey_cast = Wp_parameters.register_category "memdebug:cast" +let dkey_access = Wp_parameters.register_category "memdebug:access" +let dkey_valid = Wp_parameters.register_category "memdebug:valid" +let dkey_alias = Wp_parameters.register_category "memdebug:alias" + +let debug_cons = Wp_parameters.debug ~dkey:dkey_cons +let debug_loc = Wp_parameters.debug ~dkey:dkey_loc +let debug_cast = Wp_parameters.debug ~dkey:dkey_cast +let debug_access = Wp_parameters.debug ~dkey:dkey_access +let debug_valid = Wp_parameters.debug ~dkey:dkey_valid +let debug_alias = Wp_parameters.debug ~dkey:dkey_alias + +module Make(M : Sigs.Model) = +struct + let datatype = "MemDebug." ^ M.datatype + let configure = M.configure + + let hypotheses = M.hypotheses + + module Chunk = M.Chunk + + module Heap = M.Heap + module Sigma = M.Sigma + + type loc = M.loc + type chunk = M.chunk + type sigma = M.sigma + type segment = M.segment + type state = M.state + + (* ---------------------------------------------------------------------- *) + (* ---- Pretty-printing ---- *) + (* ---------------------------------------------------------------------- *) + + let pretty = M.pretty + + let state = M.state + let iter = M.iter + let lookup = M.lookup + let updates = M.updates + let apply = M.apply + + let vars = M.vars + let occurs = M.occurs + + (* ---------------------------------------------------------------------- *) + (* ---- Constructors ----- *) + (* ---------------------------------------------------------------------- *) + + let null = + let l = M.null in debug_cons "null:@, %a" pretty l; + M.null + + let literal ~eid cstr = + let l = M.literal ~eid cstr in + debug_cons "literal ~eid:%d ->@, %a" eid pretty l; + l + + let cvar x = + let l = M.cvar x in + debug_cons "cvar %a ->@, %a" Varinfo.pretty x pretty l; + l + + let pointer_loc e = + let l = M.pointer_loc e in + debug_cons "term2loc %a ->@, %a" pp_term e pretty l; + l + let pointer_val l = + let e = M.pointer_val l in + debug_cons "loc2term %a ->@, %a" pretty l pp_term e; + e + + (* ---------------------------------------------------------------------- *) + (* ---- Operations ---- *) + (* ---------------------------------------------------------------------- *) + + let field l fd = + let l' = M.field l fd in + debug_loc "@[%a.%a ->@, %a@]" pretty l Fieldinfo.pretty fd pretty l'; + l' + let shift l obj e = + let l' = M.shift l obj e in + debug_loc "@[%a+%a ->@, %a@]" pretty l pp_term e pretty l'; + l' + + let base_addr l = + let l' = M.base_addr l in + debug_loc "@[base_addr: %a -> %a@]" pretty l pretty l'; + l' + + let block_length = M.block_length + + (* ---------------------------------------------------------------------- *) + (* ---- Casting ----- *) + (* ---------------------------------------------------------------------- *) + + let cast ty l = + let l' = M.cast ty l in + debug_cast "(%a)%a -> %a" Ctypes.pretty ty.post pretty l pretty l'; + l' + + let loc_of_int obj e = + let l = M.loc_of_int obj e in + debug_cast "(%a)%a -> %a" Ctypes.pretty obj pp_term e pretty l; + l + let int_of_loc cint l = + let e = M.int_of_loc cint l in + debug_cast "(%a)%a -> %a" Ctypes.pp_int cint pretty l pp_term e; + e + + (* ---------------------------------------------------------------------- *) + (* ---- Domain ----- *) + (* ---------------------------------------------------------------------- *) + + let domain = M.domain + + (* ---------------------------------------------------------------------- *) + (* ---- Memory Access ----- *) + (* ---------------------------------------------------------------------- *) + + let load s obj l = + let v = M.load s obj l in + debug_access "@[load %a @, %a @, %a ->@, %a@]@." + Sigma.pretty s Ctypes.pretty obj pretty l (pp_value pretty) v; + v + + let load_init _s _obj _l = e_false + + let stored seq obj l t = + let preds = M.stored seq obj l t in + debug_access "@[stored %a@, %a@, %a@, %a ->@, %a@]@." + (pp_sequence Sigma.pretty) seq Ctypes.pretty obj pretty l pp_term t + (Pretty_utils.pp_list pp_equation) preds; + preds + + let stored_init _seq _obj _l _v = [] + + let copied seq obj ll rl = + let preds = M.copied seq obj ll rl in + debug_access "@[copied %a@, %a@, %a@, %a ->@, %a@]@." + (pp_sequence Sigma.pretty) seq Ctypes.pretty obj pretty ll pretty rl + (Pretty_utils.pp_list pp_equation) preds; + preds + + let copied_init _seq _obj _ll _rl = [] + + let assigned seq obj sloc = + let preds = M.assigned seq obj sloc in + debug_access "@[assigned %a@, %a@, %a ->@, %a@]@." + (pp_sequence Sigma.pretty) seq Ctypes.pretty obj (pp_sloc pretty) sloc + (Pretty_utils.pp_list pp_equation) preds; + preds + + (* ---------------------------------------------------------------------- *) + (* ---- Pointer Comparison ----- *) + (* ---------------------------------------------------------------------- *) + + let is_null = M.is_null + let loc_eq = M.loc_eq + let loc_lt = M.loc_lt + let loc_leq = M.loc_leq + let loc_neq = M.loc_neq + let loc_diff = M.loc_diff + + (* ---------------------------------------------------------------------- *) + (* ---- Validity ----- *) + (* ---------------------------------------------------------------------- *) + + let valid s acs seg = + let p = M.valid s acs seg in + debug_valid "@[valid %a@, %a@, %a ->@, %a@]@." + Sigma.pretty s pp_acs acs (pp_rloc pretty) seg pp_pred p; + p + + let invalid s seg = + let p = M.invalid s seg in + debug_valid "@[invalid %a@, %a ->@, %a@]@." + Sigma.pretty s (pp_rloc pretty) seg pp_pred p; + p + + let scope = M.scope + let global = M.global + + (* ---------------------------------------------------------------------- *) + (* ---- Separation ----- *) + (* ---------------------------------------------------------------------- *) + + let included seg1 seg2 = + let p = M.included seg1 seg2 in + debug_alias "@[included %a@, %a ->@, %a@]@." + (pp_rloc pretty) seg1 (pp_rloc pretty) seg2 pp_pred p; + p + + let separated seg1 seg2 = + let p = M.separated seg1 seg2 in + debug_alias "@[separated %a@, %a ->@, %a@]@." + (pp_rloc pretty) seg1 (pp_rloc pretty) seg2 pp_pred p; + p + + + (** todo *) + let initialized = M.initialized + let alloc = M.alloc + let frame = M.frame + let is_well_formed = M.is_well_formed + let base_offset = M.base_offset + type domain = M.domain + let configure_ia = M.configure_ia + +end diff --git a/src/plugins/wp/MemDebug.mli b/src/plugins/wp/MemDebug.mli new file mode 100644 index 0000000000000000000000000000000000000000..d38816004981795e510d88783c5c9d1205c5ab1f --- /dev/null +++ b/src/plugins/wp/MemDebug.mli @@ -0,0 +1,38 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- Debug Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +val pp_sequence : 'a Pretty_utils.formatter -> Format.formatter -> + 'a Sigs.sequence -> unit +val pp_equation : Format.formatter -> Sigs.equation -> unit +val pp_acs : Format.formatter -> Sigs.acs -> unit +val pp_value : 'a Pretty_utils.formatter -> Format.formatter -> + 'a Sigs.value -> unit +val pp_rloc : 'a Pretty_utils.formatter -> Format.formatter -> + 'a Sigs.rloc -> unit +val pp_sloc : 'a Pretty_utils.formatter -> Format.formatter -> + 'a Sigs.sloc -> unit + +module Make(M : Sigs.Model) : Sigs.Model diff --git a/src/plugins/wp/MemVal.ml b/src/plugins/wp/MemVal.ml new file mode 100644 index 0000000000000000000000000000000000000000..7b1be408b07648a8895a84cdf628a9c21d6e2aef --- /dev/null +++ b/src/plugins/wp/MemVal.ml @@ -0,0 +1,869 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- Value Separation Analysis' Based Memory Model --- *) +(* -------------------------------------------------------------------------- *) + +open Cil_types +open Cil_datatype +open Ctypes +open Lang +open Lang.F +open Sigs +open Definitions + +module Logic = Qed.Logic + +module type State = +sig + type t + + val bottom : t + val join : t -> t -> t + + val of_kinstr : Cil_types.kinstr -> t + val of_stmt : Cil_types.stmt -> t + val of_kf : Cil_types.kernel_function -> t + + val pretty : Format.formatter -> t -> unit +end + +module type Value = +sig + val configure : unit -> WpContext.rollback + val datatype : string + + module State : State + + type t + type state = State.t + + val null : t + val literal: eid:int -> Cstring.cst -> int * t + val cvar : varinfo -> t + + val field : t -> Cil_types.fieldinfo -> t + val shift : t -> Ctypes.c_object -> term -> t + val base_addr : t -> t + + val load : state -> t -> Ctypes.c_object -> t + + val domain : t -> Base.t list + val offset : t -> (term -> pred) + + val pretty : Format.formatter -> t -> unit +end + +module type Base = +sig +end + +let dkey = Wp_parameters.register_category "memval" (* Debugging key *) +let dkey_val = Wp_parameters.register_category "memval:val" + +let debug fmt = Wp_parameters.debug ~dkey fmt +let debug_val = Wp_parameters.debug ~dkey:dkey_val + + +(* -------------------------------------------------------------------------- *) +(* --- Logic Memory Wrapper --- *) +(* -------------------------------------------------------------------------- *) +let library = "memory" + +let a_addr = Lang.datatype ~library "addr" +let t_addr = Logic.Data(a_addr,[]) +let f_base = Lang.extern_f ~library ~result:Logic.Int + ~link:{altergo = Qed.Engine.F_subst("%1.base"); + why3 = Qed.Engine.F_subst("%1.base"); + coq = Qed.Engine.F_subst("(base %1)"); + } "base" +let f_offset = Lang.extern_f ~library ~result:Logic.Int + ~link:{altergo = Qed.Engine.F_subst("%1.offset"); + why3 = Qed.Engine.F_subst("%1.offset"); + coq = Qed.Engine.F_subst("(offset %1)"); + } "offset" +let f_shift = Lang.extern_f ~library ~result:t_addr "shift" +let f_global = Lang.extern_f ~library ~result:t_addr "global" +let f_null = Lang.extern_f ~library ~result:t_addr "null" + +let a_null = F.constant (e_fun f_null []) (* { base = 0; offset = 0 } *) +let a_base p = e_fun f_base [p] (* p -> p.offset *) +let a_offset p = e_fun f_offset [p] (* p -> p.base *) +let a_global b = e_fun f_global [b] (* b -> { base = b; offset = 0 } *) +let a_shift l k = e_fun f_shift [l;k] (* p k -> { p w/ offset = p.offset + k } *) +let a_addr b k = a_shift (a_global b) k (* b k -> { base = b; offset = k } *) + +(* -------------------------------------------------------------------------- *) +(* --- Cmath Wrapper --- *) +(* -------------------------------------------------------------------------- *) +let a_iabs i = e_fun ~result:Logic.Int Cmath.f_iabs [i] (* x -> |x| *) + +(* -------------------------------------------------------------------------- *) +(* --- MemValue Types --- *) +(* -------------------------------------------------------------------------- *) +(* Model utilities *) +let t_words = Logic.Array (Logic.Int, Logic.Int) (* TODO: A way to abstract this ? *) + +(* -------------------------------------------------------------------------- *) +(* --- Qed Simplifiers --- *) +(* -------------------------------------------------------------------------- *) +let phi_base t = match F.repr t with + | Logic.Fun (f, [p; _]) when f == f_shift -> a_base p + | Logic.Fun (f, [b]) when f == f_global -> b + | _ -> raise Not_found + +let phi_offset t = match F.repr t with + | Logic.Fun (f, [p; k]) when f == f_shift -> e_add (a_offset p) k + | Logic.Fun (f, _) when f == f_global -> F.e_zero + | _ -> raise Not_found + +let phi_shift p i = + if F.is_zero i then p + else match F.repr p with + | Logic.Fun (f, [q; j]) when f == f_shift -> F.e_fun f [q; F.e_add i j] + (* | Logic.Fun (f, [b]) when f == f_global -> a_addr b i *) + | _ -> raise Not_found + +let phi_read ~obj ~read ~write mem off = match F.repr mem with + | Logic.Fun (f, [_; o; v]) when f == write && off == o -> v + (*read_tau (write_tau m o v) o == v*) + | Logic.Fun (f, [m; o; _]) when f == write -> + let offset = a_iabs (F.e_sub off o) in + if F.eval_leq (F.e_int (Ctypes.sizeof_object obj)) offset then + F.e_fun read [m; off] + else raise Not_found + (*read_tau (write_tau m o v) o' == read m o' when |o - o'| <= sizeof(tau)*) + | _ -> raise Not_found + +let () = Context.register + begin fun () -> + F.set_builtin_1 f_base phi_base; + F.set_builtin_1 f_offset phi_offset; + F.set_builtin_2 f_shift phi_shift; + end + +(* -------------------------------------------------------------------------- *) +(* --- Utilities --- *) +(* -------------------------------------------------------------------------- *) +(* Wp utilities *) +module Cstring = +struct + include Cstring + + let str_cil ~eid cstr = + let enode = match cstr with + | C_str str -> Const (CStr str) + | W_str wstr -> Const (CWStr wstr) + in { + eid = eid; + enode = enode; + eloc = Location.unknown; + } +end + +(* Value utilities *) +module Base = +struct + include Base + + let bitsize_from_validity = function + | Invalid -> Integer.zero + | Empty -> Integer.zero + | Known (_, m) + | Unknown (_, _, m) -> Integer.succ m + | Variable { max_allocable } -> Integer.succ max_allocable + + let size_from_validity b = + Integer.(e_div (bitsize_from_validity b) eight) +end + + +(* -------------------------------------------------------------------------- *) +(* --- Memory Model --- *) +(* -------------------------------------------------------------------------- *) +module Make(V : Value) = +struct + (* -------------------------------------------------------------------------- *) + (* --- Model Parameters --- *) + (* -------------------------------------------------------------------------- *) + let datatype = "MemVal." ^ V.datatype + let configure () = + let rollback = V.configure () in + let orig_pointer = Context.push Lang.pointer t_addr in + let rollback () = + rollback (); + Context.pop Lang.pointer orig_pointer; + in + rollback + + module StateRef = + struct + let model : V.State.t Context.value = Context.create "Memval.model" + let get () = Context.get model + let update () = + try + (match WpContext.get_scope () with + | WpContext.Global -> assert false + | WpContext.Kf kf -> Context.set model (V.State.of_kf kf)) + with | Invalid_argument _ -> Context.set model (V.State.of_kinstr Kglobal) + | Kernel_function.No_Definition -> assert false + end + (* -------------------------------------------------------------------------- *) + (* --- Chunk --- *) + (* -------------------------------------------------------------------------- *) + type chunk = + | M_base of Base.t + + module Chunk = + struct + type t = chunk + let self = "MemVal.Chunk" + let hash = function + | M_base b -> 5 * Base.hash b + let equal c1 c2 = match c1, c2 with + | M_base b1, M_base b2 -> Base.equal b1 b2 + let compare c1 c2 = match c1, c2 with + | M_base b1, M_base b2 -> Base.compare b1 b2 + let pretty fmt = function + | M_base b -> Base.pretty fmt b + let tau_of_chunk = function + | M_base _ -> t_words + let basename_of_base = function + | Base.Var (vi, _) -> Format.sprintf "MVar_%s" (LogicUsage.basename vi) + | Base.CLogic_Var (_, _, _) -> assert false (* not supposed to append. *) + | Base.Null -> "MNull" + | Base.String (eid, _) -> Format.sprintf "MStr_%d" eid + | Base.Allocated (vi, _dealloc, _) -> + Format.sprintf "MAlloc_%s" (LogicUsage.basename vi) + let basename_of_chunk = function + | M_base b -> basename_of_base b + let is_framed = function + | M_base b -> + try + (match WpContext.get_scope () with + | WpContext.Global -> assert false + | WpContext.Kf kf -> Base.is_formal_or_local b (Kernel_function.get_definition kf)) + with Invalid_argument _ | Kernel_function.No_Definition -> + assert false (* by context. *) + end + + let cluster () = Definitions.cluster ~id:"MemVal" () + + module Heap = Qed.Collection.Make(Chunk) + module Sigma = Sigma.Make(Chunk)(Heap) + + type loc = { + loc_v : V.t; + loc_t : term (* of type addr *) + } + + type sigma = Sigma.t + type segment = loc rloc + + type state = unit + let state _ = () + let iter _ _ = () + let lookup _ _ = Mterm + let updates _ _ = Bag.empty + let apply _ _ = () + + let pretty fmt l = + Format.fprintf fmt "([@ t:%a,@ v:%a @])" + F.pp_term l.loc_t + V.pretty l.loc_v + + let vars _l = Vars.empty + let occurs _x _l = false + + (* -------------------------------------------------------------------------- *) + (* --- Constructors --- *) + (* -------------------------------------------------------------------------- *) + let null = { + loc_v = V.null; + loc_t = a_null; + } + + let literal ~eid cstr = + let bid, v = V.literal ~eid cstr in + { + loc_v = v; + loc_t = a_global (F.e_int bid) + } + + let cvar x = { + loc_v = V.cvar x; + loc_t = a_addr (F.e_int (Base.id (Base.of_varinfo x))) (F.e_zero); + } + + + (* -------------------------------------------------------------------------- *) + (* --- Generated Axiomatization --- *) + (* -------------------------------------------------------------------------- *) + + module Obj = + struct + include C_object + + let compare a b = + if a==b then 0 else + match a, b with + | C_pointer _, C_pointer _ -> 0 + | _ -> compare a b + end + + module Access = WpContext.Generator(Obj) + (struct + let name = "MemVal.Access" + type key = c_object + type data = lfun * lfun + + let read suffix t_mem t_data = + let result = t_data in + let lfun = Lang.generated_f ~result "read_%s" suffix in + let xw = Lang.freshvar ~basename:"w" t_mem in + let xo = Lang.freshvar ~basename:"o" Logic.Int in + let dfun = Definitions.Logic result in + let cluster = cluster () in + Definitions.define_symbol { + d_lfun = lfun; d_types = 0; + d_params = [xw; xo]; + d_definition = dfun; + d_cluster = cluster; + }; + lfun + + let write suffix t_mem t_data = + let result = t_mem in + let lfun = Lang.generated_f ~result "write_%s" suffix in + let xw = Lang.freshvar ~basename:"w" t_mem in + let xo = Lang.freshvar ~basename:"o" Logic.Int in + let xv = Lang.freshvar ~basename:"v" t_data in + let dfun = Definitions.Logic result in + let cluster = cluster () in + Definitions.define_symbol { + d_lfun = lfun; d_types = 0; + d_params = [xw; xo; xv]; + d_definition = dfun; + d_cluster = cluster; + }; + lfun + + let axiomatize ~obj:_ suffix t_mem t_data f_rd f_wr = + let name = "axiom_" ^ suffix in + let xw = Lang.freshvar ~basename:"w" t_mem in + let w = e_var xw in + let xo = Lang.freshvar ~basename:"o" Logic.Int in + let o = e_var xo in + let xv = Lang.freshvar ~basename:"v" t_data in + let v = e_var xv in + let p_write = e_fun f_wr [w; o; v] ~result:t_mem in + let p_read = e_fun f_rd [p_write; o] ~result:t_data in + let lemma = p_equal p_read v in + let cluster = cluster () in + (* if not (Wp_parameters.debug_atleast 1) then begin + * F.set_builtin_2 f_rd (phi_read ~obj ~read:f_rd ~write:f_wr) + * end; *) + Definitions.define_lemma { + l_kind = Cil_types.Admit; + l_name = name; l_types = 0; + l_triggers = []; + l_forall = [xw; xo; xv]; + l_lemma = lemma; + l_cluster = cluster; + } + + let axiomatize2 ~obj suffix t_mem t_data f_rd f_wr = + let name = "axiom_" ^ suffix ^ "_2" in + let xw = Lang.freshvar ~basename:"w" t_mem in + let w = e_var xw in + let xwo = Lang.freshvar ~basename:"xwo" Logic.Int in + let wo = e_var xwo in + let xro = Lang.freshvar ~basename:"xro" Logic.Int in + let ro = e_var xro in + let xv = Lang.freshvar ~basename:"v" t_data in + let v = e_var xv in + let p_write = e_fun f_wr [w; wo; v] ~result:t_mem in + let p_read = e_fun f_rd [p_write; ro] ~result:t_data in + let sizeof = (F.e_int (Ctypes.sizeof_object obj)) in + let offset = a_iabs (F.e_sub ro wo) in + let lemma = + F.p_imply + (F.p_leq sizeof offset) + (F.p_equal p_read (e_fun f_rd [w; ro] ~result:t_data)) + in + let cluster = cluster () in + Definitions.define_lemma { + l_kind = Cil_types.Admit; + l_name = name; l_types = 0; + l_triggers = []; + l_forall = [xw; xwo; xro; xv]; + l_lemma = lemma; + l_cluster = cluster; + } + + let generate obj = + let suffix = Ctypes.basename obj in + let t_mem = t_words in + let t_data = Lang.tau_of_object obj in + let d_read = read suffix t_mem t_data in + let d_write = write suffix t_mem t_data in + axiomatize ~obj suffix t_mem t_data d_read d_write; + axiomatize2 ~obj suffix t_mem t_data d_read d_write; + d_read, d_write + + let compile = Lang.local generate + end) + + let read obj ~mem ~offset = + F.e_fun (fst (Access.get obj)) [mem; offset] ~result:(Lang.tau_of_object obj) + let write obj ~mem ~offset ~value = + F.e_fun (snd (Access.get obj)) [mem; offset; value] ~result:t_words + + let fold_ite f l = + let rec aux = function + | [] -> assert false + | [x] -> f x + | x :: xs -> + F.e_if + (F.e_eq (a_base l.loc_t) (F.e_int (Base.id x))) + (f x) + (aux xs) + in + aux (V.domain l.loc_v) + + let fold_ite_pred f l = + let rec aux = function + | [] -> assert false + | [x] -> f x + | x :: xs -> + F.p_if + (F.p_equal (a_base l.loc_t) (F.e_int (Base.id x))) + (f x) + (aux xs) + in + aux (V.domain l.loc_v) + + (* -------------------------------------------------------------------------- *) + (* --- Logic to Location (and resp.) --- *) + (* -------------------------------------------------------------------------- *) + let pointer_loc _ = Warning.error ~source:"MemVal" "Cannot build top from EVA" + + let pointer_val l = l.loc_t + + + (* -------------------------------------------------------------------------- *) + (* --- Lifting --- *) + (* -------------------------------------------------------------------------- *) + let field l fd = + let offs = Integer.of_int (Ctypes.field_offset fd) in + { + loc_v = V.field l.loc_v fd; + loc_t = a_shift l.loc_t (F.e_bigint offs); + } + + let shift l obj k = + let size = Integer.of_int (Ctypes.sizeof_object obj) in + let offs = F.e_times size k in + { + loc_v = V.shift l.loc_v obj k; + loc_t = a_shift l.loc_t offs; + } + + let base_addr l = + { + loc_v = V.base_addr l.loc_v; + loc_t = a_addr (a_base l.loc_t) F.e_zero; + } + + let block_length _s _obj l = + let size_from_base base = + F.e_bigint Base.(size_from_validity (validity base)) + in + fold_ite size_from_base l + + (* -------------------------------------------------------------------------- *) + (* --- Casting --- *) + (* -------------------------------------------------------------------------- *) + let cast _ l = l + + let loc_of_int _ v = + if F.is_zero v then null + else + (*TODO: Reinterpret integer with Value *) + Warning.error ~source:"MemVal Model" + "Forbidden cast of int to pointer" + + let int_of_loc _ l = pointer_val l + + let domain _ l = + let d = V.domain l.loc_v in + assert (d <> []); + List.fold_left + (fun acc b -> Heap.Set.add (M_base b) acc) + Heap.Set.empty d + + (* -------------------------------------------------------------------------- *) + (* --- Memory Load --- *) + (* -------------------------------------------------------------------------- *) + let load_value sigma obj l = + let load_base base = + let mem = Sigma.value sigma (M_base base) in + let offset = a_offset l.loc_t in + read obj ~mem ~offset + in + let t = fold_ite load_base l in + begin if Wp_parameters.has_dkey dkey_val then + let v = V.load (StateRef.get ()) l.loc_v obj in + debug_val "load: %a -> %a" V.pretty l.loc_v V.pretty v + end; + Val t + + let load_loc ~assume sigma obj l = + let load_base v' base = + let mem = Sigma.value sigma (M_base base) in + let offset = a_offset l.loc_t in + let rd = read obj ~mem ~offset in + if assume then begin + let pred = V.offset v' (a_offset rd) in + Lang.assume pred (* Yet another mutable. *) + end; + rd + in + let v' = V.load (StateRef.get ()) l.loc_v obj in + let t = fold_ite (load_base v') l in + Loc { + loc_v = V.load (StateRef.get ()) l.loc_v obj; + loc_t = t; + } + + let load : sigma -> c_object -> loc -> loc value = fun sigma obj l -> + StateRef.update (); + begin match obj with + | C_int _ | C_float _ -> load_value sigma obj l + | C_pointer _ -> load_loc ~assume:true sigma obj l + | _ -> load_loc ~assume:false sigma obj l + end + + let load_init _sigma obj _loc = + e_var @@ Lang.freshvar ~basename:"i" @@ Lang.init_of_object obj + + (* -------------------------------------------------------------------------- *) + (* --- Memory Store --- *) + (* -------------------------------------------------------------------------- *) + let stored : sigma sequence -> c_object -> loc -> term -> equation list = fun seq obj l v -> + let mk_write cond base = + let wpre = Sigma.value seq.pre (M_base base) in + let wpost = Sigma.value seq.post (M_base base) in + let write = write obj ~mem:wpre ~offset:(a_offset l.loc_t) ~value:v in + F.p_equal wpost (F.e_if cond write wpre) + in + let rec store acc = function + | [] -> assert false + | [c] -> + let cond = F.e_and ((List.map (F.e_neq (a_base l.loc_t))) acc) in + [ Assert (mk_write cond c) ] + | c :: cs -> + let bid = (F.e_int (Base.id c)) in + let cond = F.e_eq (a_base l.loc_t) bid in + [ Assert (mk_write cond c) ] + @ store (bid :: acc) cs + in + store [ ] (V.domain l.loc_v) + + let stored_init _seq _obj _loc _t = [] + + let copied seq obj ll lr = + let v = match load seq.pre obj lr with + | Sigs.Val v -> v + | Sigs.Loc l -> l.loc_t + in + stored seq obj ll v + + let copied_init _seq _obj _ll _lr = [] + + let assigned _s _obj _sloc = [ Assert F.p_true ] + + + (* -------------------------------------------------------------------------- *) + (* --- Pointer Comparison --- *) + (* -------------------------------------------------------------------------- *) + let is_null l = p_equal l.loc_t a_null + + let loc_delta l1 l2 = + match F.is_equal (a_base l1.loc_t) (a_base l2.loc_t) with + | Logic.Yes -> F.e_sub (a_offset l1.loc_t) (a_offset l2.loc_t) + | Logic.Maybe | Logic.No -> + Warning.error "Can only compare pointers with same base." + + let base_eq l1 l2 = F.p_equal (a_base l1.loc_t) (a_base l2.loc_t) + let offset_cmp cmpop l1 l2 = cmpop (a_offset l1.loc_t) (a_offset l2.loc_t) + + let loc_diff _obj l1 l2 = loc_delta l1 l2 + let loc_eq l1 l2 = F.p_and (base_eq l1 l2) (offset_cmp F.p_equal l1 l2) + let loc_lt l1 l2 = F.p_lt (loc_delta l1 l2) F.e_zero + let loc_leq l1 l2 = F.p_leq (loc_delta l1 l2) F.e_zero + let loc_neq l1 l2 = F.p_neq (loc_delta l1 l2) F.e_zero + + (* -------------------------------------------------------------------------- *) + (* --- Segments --- *) + (* -------------------------------------------------------------------------- *) + + type range = + | LOC of loc * term (*size*) + | RANGE of loc * Vset.set (* offset range access from *loc* *) + + let range_of_rloc = function + | Rloc (obj, l) -> + LOC (l, F.e_int (Ctypes.sizeof_object obj)) + | Rrange (l, obj, Some a, Some b) -> + let la = shift l obj a in + let n = e_fact (Ctypes.sizeof_object obj) (F.e_range a b) in + LOC (la, n) + | Rrange (l, obj, a_opt, b_opt) -> + let f = F.e_fact (Ctypes.sizeof_object obj) in + RANGE (l, Vset.range (Option.map f a_opt) (Option.map f b_opt)) + + (* -------------------------------------------------------------------------- *) + (* --- Validity --- *) + (* -------------------------------------------------------------------------- *) + (** [vset_from_validity base] returns the logical set of all valid bytes of + [base]. **) + let vset_from_validity = function + | Base.Empty -> Vset.empty + | Base.Invalid -> Vset.singleton F.e_zero + | Base.Known (min_valid, max_valid) + | Base.Unknown (min_valid, Some max_valid, _) -> + (* valid between min_valid .. max_valid inclusive *) + let mn = F.e_bigint Integer.(e_div min_valid eight) in + let mx = F.e_bigint Integer.(e_div max_valid eight) in + Vset.range (Some mn) (Some mx) + | Base.Variable { Base.min_alloc = min_valid } -> + (* valid between 0 .. min_valid inclusive *) + let mn_valid = F.e_bigint Integer.(e_div min_valid eight) in + Vset.range (Some F.e_zero) (Some mn_valid) + | Base.Unknown (_, None, _) -> Vset.empty + + let valid_range : sigma -> acs -> range -> pred = fun _ acs r -> + let for_writing = match acs with RW -> true | RD -> false + | OBJ -> true (* TODO: *) in + let l, base_offset = match r with + | LOC (l, n) -> + let a = a_offset l.loc_t in + let b = F.e_add a (F.e_sub n F.e_one) in + l, Vset.range (Some a) (Some b) + | RANGE (l, r) -> l, Vset.lift_add (Vset.singleton l.loc_t) r + in + let valid_base set base = + if for_writing && (Base.is_read_only base) then + F.p_false + else + let base_vset = vset_from_validity (Base.validity base) in + Vset.subset set base_vset + in + fold_ite_pred (valid_base base_offset) l + + (** [valid sigma acs seg] returns the formula that tests if a given memory + segment [seg] (in bytes) is valid (according to [acs]) at memory state + [sigma]. **) + let valid : sigma -> acs -> segment -> pred = fun s acs seg -> + valid_range s acs (range_of_rloc seg) + + let invalid = fun _ _ -> F.p_true (* TODO *) + + (* -------------------------------------------------------------------------- *) + (* --- Scope --- *) + (* -------------------------------------------------------------------------- *) + let alloc_sigma : sigma -> varinfo list -> sigma = fun sigma xs -> + let alloc sigma x = + let havoc s c = Sigma.havoc_chunk s (M_base c) in + let v = V.cvar x in + List.fold_left havoc sigma (V.domain v) + in + List.fold_left alloc sigma xs + + let alloc_pred _ _ _ = [] + + let alloc sigma xs = + if xs = [] then sigma else alloc_sigma sigma xs + + let scope : sigma sequence -> scope -> varinfo list -> pred list = fun seq scope xs -> + match scope with + | Enter -> [] + | Leave -> + alloc_pred seq xs () + + let scope seq sc xs = + let preds = scope seq sc xs in + debug "[scope pre:%a post:%a xs:%a] -> preds:%a" + Sigma.pretty seq.pre + Sigma.pretty seq.post + (Pretty_utils.pp_iter ~sep:" " List.iter Varinfo.pretty) xs + (Pretty_utils.pp_iter ~sep:" " List.iter pp_pred) preds; + preds + + let global : sigma -> term (*addr*) -> pred = fun _ _ -> + F.p_true (* True is harmless and WP never call this function... *) + + + (* -------------------------------------------------------------------------- *) + (* --- Separation --- *) + (* -------------------------------------------------------------------------- *) + let range_to_base_offset = function + | LOC (l, n) -> + let a = a_offset l.loc_t in + let b = F.e_add a n in + l, Vset.range (Some a) (Some b) + | RANGE (l, r) -> l, Vset.lift_add (Vset.singleton l.loc_t) r + + let included : segment -> segment -> pred = fun s1 s2 -> + (* (b1 = b2) -> (r1 \in r2) *) + let l1, vs1 = range_to_base_offset (range_of_rloc s1) in + let l2, vs2 = range_to_base_offset (range_of_rloc s2) in + p_and + (p_equal (a_base l1.loc_t) (a_base l2.loc_t)) + (Vset.subset vs1 vs2) + + let separated : segment -> segment -> pred = fun s1 s2 -> + (* (b1 = b2) -> (r1 \cap r2) = \empty *) + let l1, vs1 = range_to_base_offset (range_of_rloc s1) in + let l2, vs2 = range_to_base_offset (range_of_rloc s2) in + p_and + (p_equal (a_base l1.loc_t) (a_base l2.loc_t)) + (Vset.disjoint vs1 vs2) + + let initialized _sigma _l = F.p_true (* todo *) + let is_well_formed _ = F.p_true (* todo *) + let base_offset _loc = assert false (** TODO *) + type domain = Sigma.domain + let no_binder = { bind = fun _ f v -> f v } + let configure_ia _ = no_binder (* todo *) + let hypotheses x = x (* todo *) + let frame _sigma = [] (* todo *) + +end + + + + +(* -------------------------------------------------------------------------- *) +(* --- EVA Instance --- *) +(* -------------------------------------------------------------------------- *) +module Eva = +struct + open Cvalue + + let datatype = "Eva" + let configure () = + if not (Db.Value.is_computed ()) then + Wp_parameters.abort ~current:true + "Could not use Eva memory model without a previous run of the analysis."; + (fun () -> ()) + + module State = + struct + type t = Model.t + + let bottom = Model.bottom + let join = Model.join + + let of_kinstr k = Db.Value.get_state k + let of_stmt k = Db.Value.get_stmt_state k + let of_kf kf = + let state = ref bottom in + let vis = object + inherit Cil.nopCilVisitor + method !vstmt stmt = + state := join (of_stmt stmt) !state; + Cil.DoChildren + end in + ignore (Cil.visitCilFunction vis (Kernel_function.get_definition kf)); + !state + + let pretty = Model.pretty + end + + type t = V.t + type state = Model.t + + let null = V.inject Base.null Ival.zero + let literal ~eid cstr = + let b = Base.of_string_exp (Cstring.str_cil ~eid cstr) in + Base.id b, V.inject b Ival.zero + let cvar x = V.inject (Base.of_varinfo x) Ival.zero + + let field v fd = + let bsize = Ctypes.field_offset fd |> Integer.of_int in + let offs = Ival.inject_singleton bsize in + Cvalue.V.shift offs v + let shift v obj t = + let bsize = 8 * Ctypes.sizeof_object obj |> Integer.of_int in + let offs = match F.repr t with + | Logic.Kint z -> Ival.inject_singleton (Integer.mul bsize z) + | _ -> Ival.top in + Cvalue.V.shift offs v + let base_addr v = + Cvalue.V.fold_topset_ok + (fun b _ v -> Cvalue.V.add b Ival.zero v) + v (Cvalue.V.bottom) + + let load state v obj = + let bsize = 8 * Ctypes.sizeof_object obj in + let bits = Locations.loc_bytes_to_loc_bits v in + let int_base = bsize |> Integer.of_int |> Int_Base.inject in + let vloc = Locations.make_loc bits int_base in + Cvalue.Model.find state vloc + + let domain v = + Cvalue.V.fold_topset_ok + (fun b _ acc -> b :: acc) + v [] + + let logic_ival ival = fun x -> + (* assert (not (Ival.is_float ival)); (* by integer offsets *) *) + match Ival.project_small_set ival with + | Some is -> + F.p_any + (fun i -> F.p_equal x (F.e_bigint i)) + is + | None -> begin + match Ival.min_and_max ival with + | Some mn, Some mx -> + F.p_and + (F.p_leq (F.e_bigint mn) x) + (F.p_leq x (F.e_bigint mx)) + | Some mn, None -> F.p_leq (F.e_bigint mn) x + | None, Some mx -> F.p_leq x (F.e_bigint mx) + | None, None -> F.p_true + end + + let offset v = fun x -> + let ivals = + Cvalue.V.fold_topset_ok + (fun _ ival acc -> ival :: acc) + v [] + in + F.p_any (fun ival -> logic_ival ival x) ivals + + let pretty = Cvalue.V.pretty + +end diff --git a/src/plugins/wp/MemVal.mli b/src/plugins/wp/MemVal.mli new file mode 100644 index 0000000000000000000000000000000000000000..db2ffb62c6f98723e2ea7d95b48229b94eb72dc1 --- /dev/null +++ b/src/plugins/wp/MemVal.mli @@ -0,0 +1,85 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Lang.F + +module type State = +sig + type t (** abstract state **) + + val bottom : t + val join : t -> t -> t + + val of_kinstr : Cil_types.kinstr -> t + (** [of_stmt stmt] get the abstract state of [stmt]. **) + val of_stmt : Cil_types.stmt -> t + (** [of_kf kf] get the join state of all [kf]'s statements states **) + val of_kf : Cil_types.kernel_function -> t + + val pretty : Format.formatter -> t -> unit +end + +module type Value = +sig + val configure : unit -> WpContext.rollback + val datatype : string + + module State : State + + type t (** abstract value **) + type state = State.t + + val null : t + (** [literal eid cstr] returns the pair of base identifier and abstract value + corresponding to the concrete string constant [cstr] of unique expression + identifier [eid]. [eid] should be a valid identifier for [cstr]. **) + val literal: eid:int -> Cstring.cst -> int * t + (** [cvar x] returns the abstract value corresponding to &[x]. **) + val cvar : Cil_types.varinfo -> t + + (** [field v fd] returns the value obtained by access to field [fd] + from [v]. **) + val field : t -> Cil_types.fieldinfo -> t + (** [shift v obj k] returns the value obtained by access at an index [k] + with type [obj] from [v]. **) + val shift : t -> Ctypes.c_object -> term -> t + (** [base_addr v] returns the value corresponding to the base address + of [v]. **) + val base_addr : t -> t + + (** [load s v obj] returns the value at the location given by [v] with type + [obj] within the state [s]. **) + val load : state -> t -> Ctypes.c_object -> t + + (** [domain v] returns a list of all possible concrete bases of [v]. **) + val domain : t -> Base.t list + (** [offset v] returns a function which when applied with a term returns + a predicate over [v]'s offset. *) + val offset : t -> (term -> pred) + + val pretty : Format.formatter -> t -> unit +end + +module Make(V : Value) : Sigs.Model + +(** The glue between WP and EVA. **) +module Eva : Value diff --git a/src/plugins/wp/share/why3/frama_c_wp/cmath.mlw b/src/plugins/wp/share/why3/frama_c_wp/cmath.mlw index 495ac75347b5f5e22420e96d2cf7afdf805c27e2..31243afd20dffa3c424f827d8b317fb1d2a12dd9 100644 --- a/src/plugins/wp/share/why3/frama_c_wp/cmath.mlw +++ b/src/plugins/wp/share/why3/frama_c_wp/cmath.mlw @@ -35,6 +35,14 @@ theory Cmath [@ W:non_conservative_extension:N] end +theory IAbs + use export int.Abs +end + +theory RAbs + use export real.Abs +end + theory Square [@ W:non_conservative_extension:N] use real.RealInfix diff --git a/src/plugins/wp/tests/wp_eva/oracle/simple.0.res.oracle b/src/plugins/wp/tests/wp_eva/oracle/simple.0.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..a16c6af1b17c2d65b17c136241a3729c63477320 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/oracle/simple.0.res.oracle @@ -0,0 +1,79 @@ +[kernel] Parsing tests/wp_eva/simple.c (with preprocessing) +[eva:alarm] tests/wp_eva/simple.c:25: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:26: Warning: + division by zero. assert (int)(*x - 42) ≢ 0; +[eva:alarm] tests/wp_eva/simple.c:31: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:35: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:41: Warning: assertion got status unknown. +[wp] Running WP plugin... +[wp] tests/wp_eva/simple.c:44: Warning: + Ignored 'terminates' specification: + \false +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function f +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 25): +Let x = read_sint32(MVar_z_0, 0). +Let x_1 = read_sint32(write_sint32(MVar_z_0, 0, 1 + x), 0). +Assume { + Type: is_sint32(x) /\ is_sint32(x_1). + (* Assertion 'Reduc' *) + Have: (2 <= x) /\ (x <= 1000). + (* Else *) + Have: x != 41. +} +Prove: P_P(x_1). + +------------------------------------------------------------ + +Goal Assertion 'Eva,division_by_zero' (file tests/wp_eva/simple.c, line 26): +Let x = read_sint32(MVar_z_0, 0). +Let x_1 = read_sint32(write_sint32(MVar_z_0, 0, 1 + x), 0). +Assume { + Type: is_sint32(x) /\ is_sint32(x_1). + (* Assertion 'Reduc' *) + Have: (2 <= x) /\ (x <= 1000). + (* Else *) + Have: x != 41. + (* Assertion *) + Have: P_P(x_1). +} +Prove: to_sint32(x_1 - 42) != 0. + +------------------------------------------------------------ +------------------------------------------------------------ + Function g +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 31): +Let x = read_sint32(MVar_x_0, 0). +Let x_1 = read_sint32(MVar_y_0, 0). +Let x_2 = read_sint32(MVar_z_0, 0). +Let x_3 = read_sint32(write_sint32(MVar_x_0, 0, 1 + x), 0). +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2) /\ is_sint32(x_3). +} +Prove: P_P(x_1) /\ P_P(x_2) /\ P_P(x_3). + +------------------------------------------------------------ +------------------------------------------------------------ + Function h +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 35): +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function h2 +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 41): +Let x = read_sint32(MVar_z_0, 0). +Let x_1 = read_sint32(write_sint32(MVar_x_0, 0, x), 0). +Assume { Type: is_sint32(x) /\ is_sint32(x_1). } +Prove: x_1 = x. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_eva/oracle/simple.1.res.oracle b/src/plugins/wp/tests/wp_eva/oracle/simple.1.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..06c9c1731bb77d88e397208938b785876af85f17 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/oracle/simple.1.res.oracle @@ -0,0 +1,84 @@ +[kernel] Parsing tests/wp_eva/simple.c (with preprocessing) +[eva:alarm] tests/wp_eva/simple.c:25: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:26: Warning: + division by zero. assert (int)(*x - 42) ≢ 0; +[eva:alarm] tests/wp_eva/simple.c:31: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:35: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:41: Warning: assertion got status unknown. +[wp] Running WP plugin... +[wp] tests/wp_eva/simple.c:44: Warning: + Ignored 'terminates' specification: + \false +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function f +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 25): +Let x = Mint_0[shift(global(Base_3_0), Offs_0)]. +Let x_1 = 1 + x. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1). + (* Assertion 'Reduc' *) + Have: (2 <= x) /\ (x <= 1000). + (* Else *) + Have: x != 41. +} +Prove: P_P(x_1). + +------------------------------------------------------------ + +Goal Assertion 'Eva,division_by_zero' (file tests/wp_eva/simple.c, line 26): +Let x = Mint_0[shift(global(Base_3_0), Offs_0)]. +Let x_1 = 1 + x. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1). + (* Assertion 'Reduc' *) + Have: (2 <= x) /\ (x <= 1000). + (* Else *) + Have: x != 41. + (* Assertion *) + Have: P_P(x_1). +} +Prove: to_sint32(x - 41) != 0. + +------------------------------------------------------------ +------------------------------------------------------------ + Function g +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 31): +Let a = shift(global(Base_9_0), Offs_0). +Let x = Mint_0[a]. +Let x_1 = 1 + x. +Let m = Mint_0[a <- x_1]. +Let x_2 = m[shift(global(Base_5_0), Offs_1)]. +Let x_3 = m[shift(global(Base_7_0), Offs_2)]. +Assume { + Type: is_sint32(x) /\ is_sint32(x_1) /\ is_sint32(x_2) /\ is_sint32(x_3). +} +Prove: P_P(x_1) /\ P_P(x_2) /\ P_P(x_3). + +------------------------------------------------------------ +------------------------------------------------------------ + Function h +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 35): +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function h2 +------------------------------------------------------------ + +Goal Assertion (file tests/wp_eva/simple.c, line 41): +Let a = shift(global(Base_16_0), Offs_0). +Let m = Mint_0[a <- (Base_14_0 = 238) & Mvar_z_0[Offs_1]] + [shift(global(Base_18_0), Offs_2) <- 1]. +Let x = m[shift(global(Base_14_0), Offs_1)]. +Let x_1 = m[a]. +Assume { Type: is_sint32(x) /\ is_sint32(x_1). } +Prove: x_1 = x. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.0.res.oracle b/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.0.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..96be82db9ffdad6c079c129a01ed837085fa8563 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.0.res.oracle @@ -0,0 +1,29 @@ +[kernel] Parsing tests/wp_eva/simple.c (with preprocessing) +[eva:alarm] tests/wp_eva/simple.c:25: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:26: Warning: + division by zero. assert (int)(*x - 42) ≢ 0; +[eva:alarm] tests/wp_eva/simple.c:31: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:35: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:41: Warning: assertion got status unknown. +# frama-c -wp -wp-model 'Eva1' [...] +[wp] Running WP plugin... +[wp] tests/wp_eva/simple.c:44: Warning: + Ignored 'terminates' specification: + \false +[wp] Warning: Missing RTE guards +[wp] 5 goals scheduled +[wp] [Alt-Ergo] Goal eva1_f_assert : Timeout (missing cache) +[wp] [Alt-Ergo] Goal eva1_f_assert_Eva_division_by_zero : Valid (missing cache) +[wp] [Alt-Ergo] Goal eva1_g_assert : Timeout (missing cache) +[wp] [Qed] Goal eva1_h_assert : Valid +[wp] [Alt-Ergo] Goal eva1_h2_assert : Valid (missing cache) +[wp] Proved goals: 3 / 5 + Qed: 1 + Alt-Ergo: 2 (unsuccess: 2) +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f - 1 2 50.0% + g - - 1 0.0% + h 1 - 1 100% + h2 - 1 1 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.1.res.oracle b/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.1.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..446ecb89db8387c9d40d6657c15413446bce0e43 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/oracle_qualif/simple.1.res.oracle @@ -0,0 +1,30 @@ +[kernel] Parsing tests/wp_eva/simple.c (with preprocessing) +[eva:alarm] tests/wp_eva/simple.c:25: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:26: Warning: + division by zero. assert (int)(*x - 42) ≢ 0; +[eva:alarm] tests/wp_eva/simple.c:31: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:35: Warning: assertion got status unknown. +[eva:alarm] tests/wp_eva/simple.c:41: Warning: assertion got status unknown. +# frama-c -wp -wp-model 'Eva2' [...] +[wp] Running WP plugin... +[wp] tests/wp_eva/simple.c:44: Warning: + Ignored 'terminates' specification: + \false +[wp] Warning: Missing RTE guards +[wp] 5 goals scheduled +[wp] [Alt-Ergo] Goal eva2_f_assert : Timeout (missing cache) +[wp] [Alt-Ergo] Goal eva2_f_assert_Eva_division_by_zero : Valid (missing cache) +[wp] [Alt-Ergo] Goal eva2_g_assert : Timeout (missing cache) +[wp] [Qed] Goal eva2_h_assert : Valid +[wp] [Alt-Ergo] Goal eva2_h2_assert : Failed + [Why3 Error] anomaly: File "src/plugins/wp/ProverWhy3.ml", line 609, characters 9-15: Assertion failed +[wp] Proved goals: 2 / 5 + Qed: 1 + Alt-Ergo: 1 (unsuccess: 2) (failed: 1) +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f - 1 2 50.0% + g - - 1 0.0% + h 1 - 1 100% + h2 - - 1 0.0% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_eva/simple.c b/src/plugins/wp/tests/wp_eva/simple.c new file mode 100644 index 0000000000000000000000000000000000000000..4de74124ec0da6495455fdcbee98adb3362cc826 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/simple.c @@ -0,0 +1,52 @@ +/* run.config + OPT: -wp-model eva1 + OPT: -wp-model eva2 +*/ + +/* run.config_qualif + OPT: -wp-model eva1 + OPT: -wp-model eva2 +*/ + +#include "__fc_builtin.h" + +/*@ + axiomatic P { + predicate P(int x); + } + + @*/ + +int z; + +void f (int *x, int *y){ + if(z==41) return; + *x = *x+1; + /*@ assert P(*x) && P(*y) && P(z); @*/ + int r = 1/(*x-42); +} + +void g (int *x, int *y){ + *x = *x+1; + /*@ assert P(*x) && P(*y) && P(z); @*/ +} + +void h (int *x, int *y){ + /*@ assert *x == *y; */ +} + +void h2 (int *x, int *y, int *z){ + *x = *z; + *y = 1; + /*@ assert *x == *z; */ +} + +void main(){ + int x = Frama_C_interval(2,1000); + int y = Frama_C_interval(2,1000); + z = Frama_C_interval(2,1000); + f(&z,&z); + g(&x,&y); + h(&x,&x); + h2(&x,&y,&z); +} diff --git a/src/plugins/wp/tests/wp_eva/test_config b/src/plugins/wp/tests/wp_eva/test_config new file mode 100644 index 0000000000000000000000000000000000000000..8b6102546cc5b9169aa4d10d195b2ef2f5bab0b3 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/test_config @@ -0,0 +1 @@ +CMD: @frama-c@ -no-autoload-plugins -load-module eva,scope,reduc,wp -eva -eva-no-print -eva-verbose 0 @PTEST_FILE@ -then -reduc -reduc-gen-annot all -then -no-reduc -wp -wp-print -wp-prover none -wp-share ./share -wp-msg-key shell diff --git a/src/plugins/wp/tests/wp_eva/test_config_qualif b/src/plugins/wp/tests/wp_eva/test_config_qualif new file mode 100644 index 0000000000000000000000000000000000000000..153e5ddde393c7008e0ff327852ba9b948a25e54 --- /dev/null +++ b/src/plugins/wp/tests/wp_eva/test_config_qualif @@ -0,0 +1,2 @@ +CMD: @frama-c@ -no-autoload-plugins -load-module eva,scope,reduc,wp -eva -eva-no-print -eva-verbose 0 @PTEST_FILE@ -then -reduc -reduc-gen-annot all -then -no-reduc -then -wp -wp-par 1 -wp-share ./share -wp-msg-key shell -wp-report tests/qualif.report -wp-session @PTEST_DIR@/oracle@PTEST_CONFIG@/@PTEST_NAME@.@PTEST_NUMBER@.session -wp-cache-env -wp-cache replay @PTEST_FILE@ -wp-coq-timeout 120 +OPT: diff --git a/src/plugins/wp/wp_parameters.ml b/src/plugins/wp/wp_parameters.ml index dbb9b389fb6182b69bc1cfcf5ed68d84b3c39a96..8ec961641c448e59acd628407ce46e54f2ea59a5 100644 --- a/src/plugins/wp/wp_parameters.ml +++ b/src/plugins/wp/wp_parameters.ml @@ -205,7 +205,8 @@ module Model = * '+raw' no logic variable\n\ * '+ref' by-reference-style pointers detection\n\ * '+nat/+int' natural / machine-integers arithmetics\n\ - * '+real/+float' real / IEEE floating point arithmetics" + * '+real/+float' real / IEEE floating point arithmetics\n\ + * 'Eva' (experimental) based on the results from Eva plugin" end) let () = Parameter_customize.set_group wp_model