From 30edfb290696f20c4e7afbcd693bc9d4c89f6b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Wed, 13 Jul 2022 14:57:41 +0200 Subject: [PATCH] [Eva] Tracks the generated Eva.mli file. As it is the public interface of Eva, it should be readily available in the repository. --- .gitignore | 1 - src/plugins/eva/Eva.mli | 640 ++++++++++++++++++++++++++++++++++++++++ src/plugins/eva/dune | 1 + 3 files changed, 641 insertions(+), 1 deletion(-) create mode 100644 src/plugins/eva/Eva.mli diff --git a/.gitignore b/.gitignore index 49567453cf9..d51531fef5e 100644 --- a/.gitignore +++ b/.gitignore @@ -208,7 +208,6 @@ Makefile.plugin.generated /src/plugins/gui/GSourceView.mli /src/plugins/gui/gtk_compat.ml /src/plugins/markdown-report/META -/src/plugins/eva/Eva.mli # generated tar.gz files diff --git a/src/plugins/eva/Eva.mli b/src/plugins/eva/Eva.mli new file mode 100644 index 00000000000..2a4d5f6dbd1 --- /dev/null +++ b/src/plugins/eva/Eva.mli @@ -0,0 +1,640 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* 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). *) +(* *) +(**************************************************************************) + +(** Eva public API. + + The main modules are: + - Analysis: run the analysis. + - Results: access analysis results, especially the values of expressions + and memory locations of lvalues at each program point. + + The following modules allow configuring the Eva analysis: + - Parameters: change the configuration of the analysis. + - Eva_annotations: add local annotations to guide the analysis. + - Builtins: register ocaml builtins to be used by the cvalue domain + instead of analysing the body of some C functions. + + Other modules are for internal use only. *) + +(* This file is generated. Do not edit. *) + +module Analysis: sig + val compute : unit -> unit + (** Computes the Eva analysis, if not already computed, using the entry point + of the current project. You may set it with {!Globals.set_entry_point}. + @raise Globals.No_such_entry_point if the entry point is incorrect + @raise Db.Value.Incorrect_number_of_arguments if some arguments are + specified for the entry point using {!Db.Value.fun_set_args}, and + an incorrect number of them is given. + @see <https://frama-c.com/download/frama-c-plugin-development-guide.pdf> Plug-in Development Guide *) + + val is_computed : unit -> bool + (** Return [true] iff the Eva analysis has been done. + @plugin development guide + *) + + val self : State.t + (** Internal state of Eva analysis from projects viewpoint. *) + + type computation_state = NotComputed | Computing | Computed | Aborted + (** Computation state of the analysis. *) + + val current_computation_state : unit -> computation_state + (** Get the current computation state of the analysis, updated by + [force_compute] and states updates. *) + + val register_computation_hook: ?on:computation_state -> + (computation_state -> unit) -> unit + (** Registers a hook that will be called each time the analysis starts or + finishes. If [on] is given, the hook will only be called when the + analysis switches to this specific state. *) + + (** Kind of results for the analysis of a function body. *) + type results = + | Complete + (** The results are complete: they cover all possible call contexts of the + given function. *) + | Partial + (** The results are partial, as the functions has not been analyzed in all + possible call contexts. This happens for recursive functions that are + not completely unrolled, or if the analysis has stopped unexpectedly. *) + | NoResults + (** No results were saved for the function, due to option -eva-no-results. + Any request at a statement of this function will lead to a Top result. *) + + (* Analysis status of a function. *) + type status = + | Unreachable + (** The function has not been reached by the analysis. Any request in this + function will lead to a Bottom result. *) + | SpecUsed + (** The function specification has been used to interpret its calls: + its body has not been analyzed. Any request at a statement of this + function will lead to a Bottom result. *) + | Builtin of string + (** The builtin of the given name has been used to interpret the function: + its body has not been analyzed. Any request at a statement of this + function will lead to a Bottom result. *) + | Analyzed of results + (** The function body has been analyzed. *) + + (** Returns the analysis status of a given function. *) + val status: Cil_types.kernel_function -> status + + (** Does the analysis ignores the body of a given function, and uses instead + its specification or a builtin to interpret it? + Please use {!Eva.Results.are_available} instead to known whether results + are available for a given function. *) + val use_spec_instead_of_definition: Cil_types.kernel_function -> bool + + (** Returns [true] if the user has requested that no results should be recorded + for the given function. Please use {!Eva.Results.are_available} instead + to known whether results are available for a given function. *) + val save_results: Cil_types.kernel_function -> bool +end + +module Results: sig + + (** Eva's result API is a new interface to access the results of an analysis, + once it is completed. It may slightly change in the future. It aims at + replacing most uses of [Db.Value], and has some advantages over Db's : + + - evaluations uses every available domains and not only Cvalue; + - the caller may distinguish failure cases when a request is unsucessful; + - working with callstacks is easy; + - some common shortcuts are provided (e.g. for extracting ival directly); + - overall, individual functions are simpler. + + The idea behind this API is that requests must be decomposed in several + steps. For instance, to evaluate an expression : + + 1. first, you have to state where you want to evaluate it, + 2. optionally, you may specify in which callstack, + 3. you choose the expression to evaluate, + 4. you require a destination type to evaluate into. + + Usage sketch : + + Eva.Results.( + before stmt |> in_callstack cs |> + eval_var vi |> as_int |> default 0) + + or equivalently, if you prefer + + Eva.Results.( + default O (as_int (eval_var vi (in_callstack cs (before stmt)))) + *) + + (** Are results available for a given function? True if the function body has + been has been analyzed and the results have been saved. + False if: + - the function has not been reached by the analysis: all requests in the + function will lead to a Bottom error. + - a specification or a builtin has been used instead of analyzing the + function body: all requests in the function will lead to a Bottom error. + - results have not been saved, due to the [-eva-no-results] parameter: + all requests in the function will lead to a Top error. *) + val are_available: Cil_types.kernel_function -> bool + + type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list + + type request + + type value + type address + type 'a evaluation + + type error = Bottom | Top | DisabledDomain + type 'a result = ('a,error) Result.t + + + (** Results handling *) + + (** Translates an error to a human readable string. *) + val string_of_error : error -> string + + (** Pretty printer for errors. *) + val pretty_error : Format.formatter -> error -> unit + + (** Pretty printer for API's results. *) + val pretty_result : (Format.formatter -> 'a -> unit) -> + Format.formatter -> 'a result -> unit + + (** [default d r] extracts the value of [r] if [r] is Ok, + or use the default value [d] otherwise. + Equivalent to [Result.value ~default:d r] *) + val default : 'a -> 'a result -> 'a + + + (** Control point selection *) + + (** At the start of the analysis, but after the initialization of globals. *) + val at_start : request + + (** At the end of the analysis, after the main function has returned. *) + val at_end : unit -> request + + (** At the start of the given function. *) + val at_start_of : Cil_types.kernel_function -> request + + (** At the end of the given function. + @raises Kernel_function.No_statement if the function has no body. *) + val at_end_of : Cil_types.kernel_function -> request + + (** Just before a statement is executed. *) + val before : Cil_types.stmt -> request + + (** Just after a statement is executed. *) + val after : Cil_types.stmt -> request + + (** Just before a statement or at the start of the analysis. *) + val before_kinstr : Cil_types.kinstr -> request + + + (** Callstack selection *) + + (** Only consider the given callstack. + Replaces previous calls to [in_callstack] or [in_callstacks]. *) + val in_callstack : callstack -> request -> request + + (** Only consider the callstacks from the given list. + Replaces previous calls to [in_callstack] or [in_callstacks]. *) + val in_callstacks : callstack list -> request -> request + + (** Only consider callstacks satisfying the given predicate. Several filters + can be added. If callstacks are also selected with [in_callstack] or + [in_callstacks], only the selected callstacks will be filtered. *) + val filter_callstack : (callstack -> bool) -> request -> request + + + (** Working with callstacks *) + + (** Returns the list of reachable callstacks from the given request. + An empty list is returned if the request control point has not been + reached by the analysis, or if no information has been saved at this point + (for instance with the -eva-no-results option). + Use [is_empty request] to distinguish these two cases. *) + val callstacks : request -> callstack list + + (** Returns a list of subrequests for each reachable callstack from + the given request. *) + val by_callstack : request -> (callstack * request) list + + + (** State requests *) + + (** Returns the list of expressions which have been inferred to be equal to + the given expression by the Equality domain. *) + val equality_class : Cil_types.exp -> request -> Cil_types.exp list result + + (** Returns the Cvalue state. Error cases are converted into the bottom or top + cvalue state accordingly. *) + val get_cvalue_model : request -> Cvalue.Model.t + + (** Returns the Cvalue model. *) + val get_cvalue_model_result : request -> Cvalue.Model.t result + + (** Returns a textual representation of the internal domain states for the given + request. If [filter] is provided, states are filtered on the given bases + (for domains that support this feature). + Returns a list of pair (name, state) for all available domains. *) + val print_states: ?filter:Base.Hptset.t -> request -> (string * string) list + + (** Dependencies *) + + (** Computes (an overapproximation of) the memory zones that must be read to + evaluate the given expression, including all adresses computations. *) + val expr_deps : Cil_types.exp -> request -> Locations.Zone.t + + (** Computes (an overapproximation of) the memory zones that must be read to + evaluate the given lvalue, including the lvalue zone itself. *) + val lval_deps : Cil_types.lval -> request -> Locations.Zone.t + + (** Computes (an overapproximation of) the memory zones that must be read to + evaluate the given lvalue, excluding the lvalue zone itself. *) + val address_deps : Cil_types.lval -> request -> Locations.Zone.t + + + (** Evaluation *) + + (** Returns (an overapproximation of) the possible values of the variable. *) + val eval_var : Cil_types.varinfo -> request -> value evaluation + + (** Returns (an overapproximation of) the possible values of the lvalue. *) + val eval_lval : Cil_types.lval -> request -> value evaluation + + (** Returns (an overapproximation of) the possible values of the expression. *) + val eval_exp : Cil_types.exp -> request -> value evaluation + + + (** Returns (an overapproximation of) the possible addresses of the lvalue. *) + val eval_address : ?for_writing:bool -> + Cil_types.lval -> request -> address evaluation + + + (** Returns the kernel functions into which the given expression may evaluate. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the callee expression is not an lvalue + without offset. + Also see [callee] for a function which applies directly on Call + statements. *) + val eval_callee : Cil_types.exp -> request -> Kernel_function.t list result + + + (** Evaluated values conversion *) + + (** In all functions below, if the abstract value inferred by Eva does not fit + in the required type, [Error Top] is returned, as Top is the only possible + over-approximation of the request. *) + + (** Converts the value into a singleton ocaml int. *) + val as_int : value evaluation -> int result + + (** Converts the value into a singleton unbounded integer. *) + val as_integer : value evaluation -> Integer.t result + + (** Converts the value into a floating point number. *) + val as_float : value evaluation -> float result + + (** Converts the value into a C number abstraction. *) + val as_ival : value evaluation -> Ival.t result + + (** Converts the value into a floating point abstraction. *) + val as_fval : value evaluation -> Fval.t result + + (** Converts the value into a Cvalue abstraction. Converts error cases + into bottom and top values accordingly. *) + val as_cvalue : value evaluation -> Cvalue.V.t + + (** Converts the value into a Cvalue abstraction. *) + val as_cvalue_result : value evaluation -> Cvalue.V.t result + + (** Converts the value into a Cvalue abstraction with 'undefined' and 'escaping + addresses' flags. *) + val as_cvalue_or_uninitialized : value evaluation -> Cvalue.V_Or_Uninitialized.t + + + (** Converts into a C location abstraction. *) + val as_location : address evaluation -> Locations.location result + + (** Converts into a Zone. Error cases are converted into bottom or top zones + accordingly. *) + val as_zone: address evaluation -> Locations.Zone.t + + (** Converts into a Zone result. *) + val as_zone_result : address evaluation -> Locations.Zone.t result + + + (** Evaluation properties *) + + + (** Does the evaluated abstraction represents only one possible C value or + memory location? *) + val is_singleton : 'a evaluation -> bool + + (** Returns whether the evaluated value is initialized or not. If the value have + been evaluated from a Cil expression, it is always initialized. *) + val is_initialized : value evaluation -> bool + + (** Returns the set of alarms emitted during the evaluation. *) + val alarms : 'a evaluation -> Alarms.t list + + + (** Reachability *) + + (** Returns true if there are no reachable states for the given request. *) + val is_empty : request -> bool + + (** Returns true if an evaluation leads to bottom, i.e. if the given expression + or lvalue cannot be evaluated to a valid result for the given request. *) + val is_bottom : 'a evaluation -> bool + + (** Returns true if the function has been analyzed. *) + val is_called : Cil_types.kernel_function -> bool + + (** Returns true if the statement has been reached by the analysis. *) + val is_reachable : Cil_types.stmt -> bool + + (** Returns true if the statement has been reached by the analysis, or if + the main function has been analyzed for [Kglobal]. *) + val is_reachable_kinstr : Cil_types.kinstr -> bool + + val condition_truth_value: Cil_types.stmt -> bool * bool + (** Provided [stmt] is an 'if' construct, [fst (condition_truth_value stmt)] + (resp. snd) is true if and only if the condition of the 'if' has been + evaluated to true (resp. false) at least once during the analysis. *) + + (*** Callers / Callees / Callsites *) + + (** Returns the list of inferred callers of the given function. *) + val callers : Cil_types.kernel_function -> Cil_types.kernel_function list + + (** Returns the list of inferred callers, and for each of them, the list + of callsites (the call statements) inside. *) + val callsites : Cil_types.kernel_function -> + (Cil_types.kernel_function * Cil_types.stmt list) list + + + (** Returns the kernel functions called in the given statement. + If the callee expression doesn't always evaluate to a function, those + spurious values are ignored. If it always evaluate to a non-function value + then the returned list is empty. + Raises [Stdlib.Invalid_argument] if the statement is not a [Call] + instruction or a [Local_init] with [ConsInit] initializer. *) + val callee : Cil_types.stmt -> Kernel_function.t list + +end + +module Parameters: sig + + (** Configuration of the analysis. *) + + (** Returns the list (name, descr) of currently enabled abstract domains. *) + val enabled_domains: unit -> (string * string) list + + (** [use_builtin kf name] instructs the analysis to use the builtin [name] + to interpret calls to function [kf]. + Raises [Not_found] if there is no builtin of name [name]. *) + val use_builtin: Cil_types.kernel_function -> string -> unit + + (** [use_global_value_partitioning vi] instructs the analysis to use + value partitioning on the global variable [vi]. *) + val use_global_value_partitioning: Cil_types.varinfo -> unit +end + +module Eva_annotations: sig + + (** Register special annotations to locally guide the Eva analysis: + + - slevel annotations: "slevel default", "slevel merge" and "slevel i" + - loop unroll annotations: "loop unroll term" + - value partitioning annotations: "split term" and "merge term" + - subdivision annotations: "subdivide i" + *) + + (** Annotations tweaking the behavior of the -eva-slevel parameter. *) + type slevel_annotation = + | SlevelMerge (** Join all states separated by slevel. *) + | SlevelDefault (** Use the limit defined by -eva-slevel. *) + | SlevelLocal of int (** Use the given limit instead of -eva-slevel. *) + | SlevelFull (** Remove the limit of number of separated states. *) + + (** Loop unroll annotations. *) + type unroll_annotation = + | UnrollAmount of Cil_types.term (** Unroll the n first iterations. *) + | UnrollFull (** Unroll amount defined by -eva-default-loop-unroll. *) + + type split_kind = Static | Dynamic + + type split_term = + | Expression of Cil_types.exp + | Predicate of Cil_types.predicate + + (** Split/merge annotations for value partitioning. *) + type flow_annotation = + | FlowSplit of split_term * split_kind + (** Split states according to a term. *) + | FlowMerge of split_term + (** Merge states separated by a previous split. *) + + type allocation_kind = By_stack | Fresh | Fresh_weak | Imprecise + + type array_segmentation = + Cil_types.varinfo * Cil_types.offset * Cil_types.exp list + + type domain_scope = + string (* domain *) * + Cil_types.varinfo list (* variables that must be tracked by the domain *) + + val get_slevel_annot : Cil_types.stmt -> slevel_annotation option + val get_unroll_annot : Cil_types.stmt -> unroll_annotation list + val get_flow_annot : Cil_types.stmt -> flow_annotation list + val get_subdivision_annot : Cil_types.stmt -> int list + val get_allocation: Cil_types.stmt -> allocation_kind + + val add_slevel_annot : emitter:Emitter.t -> + Cil_types.stmt -> slevel_annotation -> unit + val add_unroll_annot : emitter:Emitter.t -> + Cil_types.stmt -> unroll_annotation -> unit + val add_flow_annot : emitter:Emitter.t -> + Cil_types.stmt -> flow_annotation -> unit + val add_subdivision_annot : emitter:Emitter.t -> + Cil_types.stmt -> int -> unit + val add_array_segmentation : emitter:Emitter.t -> + Cil_types.stmt -> array_segmentation -> unit + val add_domain_scope : emitter:Emitter.t -> + Cil_types.stmt -> domain_scope -> unit +end + +module Eval: sig + + (** Can the results of a function call be cached with memexec? *) + type cacheable = + | Cacheable (** Functions whose result can be safely cached. *) + | NoCache (** Functions whose result should not be cached, but for + which the caller can still be cached. Typically, + functions printing something during the analysis. *) + | NoCacheCallers (** Functions for which neither the call, neither the + callers, can be cached. *) +end + +module Builtins: sig + + (** Eva analysis builtins for the cvalue domain, more efficient than their + equivalent in C. *) + + open Cil_types + + exception Invalid_nb_of_args of int + exception Outside_builtin_possibilities + + (* Signature of a builtin: type of the result, and type of the arguments. *) + type builtin_type = unit -> typ * typ list + + (** Can the results of a builtin be cached? See {!Eval} for more details.*) + type cacheable = Eval.cacheable = Cacheable | NoCache | NoCacheCallers + + type full_result = { + c_values: (Cvalue.V.t option * Cvalue.Model.t) list; + (** A list of results, consisting of: + - the value returned (ie. what is after the 'return' C keyword) + - the memory state after the function has been executed. *) + c_clobbered: Base.SetLattice.t; + (** An over-approximation of the bases in which addresses of local variables + might have been written *) + c_from: (Function_Froms.froms * Locations.Zone.t) option; + (** If not None, the froms of the function, and its sure outputs; + i.e. the dependencies of the result and of each zone written to. *) + } + + (** The result of a builtin can be given in different forms. *) + type call_result = + | States of Cvalue.Model.t list + (** A disjunctive list of post-states at the end of the C function. + Can only be used if the C function does not write the address of local + variables, does not read other locations than the call arguments, and + does not write other locations than the result. *) + | Result of Cvalue.V.t list + (** A disjunctive list of resulting values. The specification is used to + compute the post-state, in which the result is replaced by the values + computed by the builtin. *) + | Full of full_result + (** See [full_result] type. *) + + (** Type of a cvalue builtin, whose arguments are: + - the memory state at the beginning of the function call; + - the list of arguments of the function call. *) + type builtin = Cvalue.Model.t -> (exp * Cvalue.V.t) list -> call_result + + (** [register_builtin name ?replace ?typ cacheable f] registers the function [f] + as a builtin to be used instead of the C function of name [name]. + If [replace] is provided, the builtin is also used instead of the C function + of name [replace], unless option -eva-builtin-auto is disabled. + If [typ] is provided, consistency between the expected [typ] and the type of + the C function is checked before using the builtin. + The results of the builtin are cached according to [cacheable]. *) + val register_builtin: + string -> ?replace:string -> ?typ:builtin_type -> cacheable -> builtin -> unit + + (** Has a builtin been registered with the given name? *) + val is_builtin: string -> bool +end + +module Cvalue_callbacks: sig + + (** Register actions to performed during the Eva analysis, + with access to the states of the cvalue domain. *) + + type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list + type state = Cvalue.Model.t + + type analysis_kind = + [ `Builtin of Value_types.call_froms + | `Spec of Cil_types.funspec + | `Def + | `Memexec ] + + (** Registers a function to be applied at the beginning of the analysis of each + function call. Arguments of the callback are the callstack of the call, + the function called, the kind of analysis performed by Eva for this call, + and the cvalue state at the beginning of the call. *) + val register_call_hook: + (callstack -> Cil_types.kernel_function -> analysis_kind -> state -> unit) + -> unit + + + type state_by_stmt = (state Cil_datatype.Stmt.Hashtbl.t) Lazy.t + type results = { before_stmts: state_by_stmt; after_stmts: state_by_stmt } + + (** Results of a function call. *) + type call_results = + | Store of results * int + (** Cvalue states before and after each statement of the given function, + plus a unique integer id for the call. *) + | Reuse of int + (** The results are the same as a previous call with the given integer id, + previously recorded with the [Store] constructor. *) + + (** Registers a function to be applied at the end of the analysis of each + function call. Arguments of the callback are the callstack of the call, + the function called and the cvalue states resulting from its analysis. *) + val register_call_results_hook: + (callstack -> Cil_types.kernel_function -> call_results -> unit) + -> unit + +end + +module Eval_terms: sig + + (** [annot_predicate_deps ~pre ~here p] computes the logic dependencies needed + to evaluate the predicate [p] in a code annotation in cvalue state [here], + in a function whose pre-state is [pre]. + Returns None on either an evaluation error or on unsupported construct. *) + val annot_predicate_deps: + pre:Cvalue.Model.t -> here:Cvalue.Model.t -> + Cil_types.predicate -> Locations.Zone.t option +end + +module Eva_results: sig + type results + + val get_results: unit -> results + val set_results: results -> unit + val merge: results -> results -> results + val change_callstacks: + (Value_types.callstack -> Value_types.callstack) -> results -> results + (** Change the callstacks for the results for which this is meaningful. + For technical reasons, the top of the callstack must currently + be preserved. *) + +end + +module Unit_tests: sig + + (** Currently tested by this module: + - semantics of sign values. *) + + (** Runs some programmatic tests on Eva. *) + val run: unit -> unit +end diff --git a/src/plugins/eva/dune b/src/plugins/eva/dune index cc13a58ece9..6a577daf2cd 100644 --- a/src/plugins/eva/dune +++ b/src/plugins/eva/dune @@ -85,6 +85,7 @@ (rule (targets Eva.ml Eva.mli) + (mode (promote (only Eva.mli))) (deps gen-api.sh Eva.header engine/analysis.mli utils/results.mli parameters.mli utils/eva_annotations.mli eval.mli -- GitLab