diff --git a/src/kernel_services/plugin_entry_points/db.ml b/src/kernel_services/plugin_entry_points/db.ml index d179e9ce4fc46b632a7f76c2579300b0e5156033..3cad7ef4576845f79aea542c9a061d4360a4a94e 100644 --- a/src/kernel_services/plugin_entry_points/db.ml +++ b/src/kernel_services/plugin_entry_points/db.ml @@ -448,7 +448,7 @@ module Value = struct module Call_Type_Value_Callbacks = Hook.Build(struct - type t = [`Builtin of Value_types.call_result | `Spec of funspec + type t = [`Builtin of Value_types.call_froms | `Spec of funspec | `Def | `Memexec] * state * (kernel_function * kinstr) list end) ;; @@ -628,19 +628,6 @@ module Value = struct let access = mk_fun "Value.access" let access_expr = mk_fun "Value.access_expr" - (** Type for a Value builtin function *) - - type builtin_type = unit -> typ * typ list - type builtin = - state -> - (Cil_types.exp * Cvalue.V.t * Cvalue.V_Offsetmap.t) list -> - Value_types.call_result - - exception Outside_builtin_possibilities - let register_builtin = mk_fun "Value.register_builtin" - let registered_builtins = mk_fun "Value.registered_builtins" - let mem_builtin = mk_fun "Value.mem_builtin" - let use_spec_instead_of_definition = mk_fun "Value.use_spec_instead_of_definition" diff --git a/src/kernel_services/plugin_entry_points/db.mli b/src/kernel_services/plugin_entry_points/db.mli index ee87e6e60202bbcaba1410a9f3e0266774b9c298..c12c38ce188ba445a909d6179d8291f2285932fa 100644 --- a/src/kernel_services/plugin_entry_points/db.mli +++ b/src/kernel_services/plugin_entry_points/db.mli @@ -175,40 +175,6 @@ module Value : sig (** {3 Parameterization} *) - exception Outside_builtin_possibilities - - (* Type of a C function interpreted by a builtin: - return type and list of argument types. *) - type builtin_type = unit -> typ * typ list - - (** Type for an Eva builtin function *) - type builtin = - (** Memory state at the beginning of the function *) - state -> - (** Args for the function: the expressions corresponding to the formals - of the functions at the call site, the actual value of those formals, - and a more precise view of those formals using offsetmaps (for eg. - structs) *) - (Cil_types.exp * Cvalue.V.t * Cvalue.V_Offsetmap.t) list -> - Value_types.call_result - - val register_builtin: - (string -> ?replace:string -> ?typ:builtin_type -> builtin -> unit) ref - (** [!register_builtin name ?replace ?typ f] registers an abstract function - [f] to use everytime a C function named [name] of type [typ] is called in - the program. If [replace] is supplied and option [-eva-builtins-auto] is - active, calls to [replace] will also be substituted by the builtin. See - also option [-eva-builtin] *) - - val registered_builtins: (unit -> (string * builtin) list) ref - (** Returns a list of the pairs (name, builtin) registered via - [register_builtin]. - @since Aluminium-20160501 *) - - val mem_builtin: (string -> bool) ref - (** returns whether there is an abstract function registered by - {!register_builtin} with the given name. *) - val use_spec_instead_of_definition: (kernel_function -> bool) ref (** To be called by derived analyses to determine if they must use the body of the function (if available), or only its spec. Used for @@ -500,7 +466,7 @@ module Value : sig @since Aluminium-20160501 *) module Call_Type_Value_Callbacks: Hook.Iter_hook with type param = - [`Builtin of Value_types.call_result | `Spec of funspec | `Def | `Memexec] + [`Builtin of Value_types.call_froms | `Spec of funspec | `Def | `Memexec] * state * callstack diff --git a/src/plugins/aorai/aorai_eva_analysis.enabled.ml b/src/plugins/aorai/aorai_eva_analysis.enabled.ml index 518a8bf3d49c305391b7b1d7788e5aacce6f6a79..c4c50da4b0eea223343f0fae79e8a77914cffe76 100644 --- a/src/plugins/aorai/aorai_eva_analysis.enabled.ml +++ b/src/plugins/aorai/aorai_eva_analysis.enabled.ml @@ -34,7 +34,7 @@ let show_aorai_variable state fmt var_name = Z.Overflow | Not_found -> Format.fprintf fmt "?" -let show_val fmt (expr, v, _) = +let show_val fmt (expr, v) = Format.fprintf fmt "%a in %a" Printer.pp_exp expr (Cvalue.V.pretty_typ (Some (Cil.typeOf expr))) v @@ -56,19 +56,14 @@ let builtin_show_aorai_state state args = end; end; (* Return value : returns nothing, changes nothing *) - { - Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } + Eva.Builtins.States [state] let () = Cil_builtins.add_custom_builtin (fun () -> (show_aorai_state,Cil.voidType,[],true)) let () = - !Db.Value.register_builtin show_aorai_state builtin_show_aorai_state + Eva.Builtins.register_builtin show_aorai_state Cacheable builtin_show_aorai_state let add_slevel_annotation vi kind = match kind with diff --git a/src/plugins/from/callwise.ml b/src/plugins/from/callwise.ml index 1e6f33366a86b33af63958b9eb4f6da4527a84e5..454e2c0953ee3f215766687de9c8618fd7875bef 100644 --- a/src/plugins/from/callwise.ml +++ b/src/plugins/from/callwise.ml @@ -90,9 +90,9 @@ let call_for_individual_froms (call_type, value_initial_state, call_stack) = call_froms_stack := { current_function; value_initial_state; table_for_calls } :: !call_froms_stack - | `Builtin { Value_types.c_from = Some (result,_) } -> + | `Builtin (Some (result,_)) -> register_from result - | `Builtin { Value_types.c_from = None } -> + | `Builtin None -> let behaviors = !Db.Value.valid_behaviors current_function value_initial_state in diff --git a/src/plugins/inout/operational_inputs.ml b/src/plugins/inout/operational_inputs.ml index d0e4af644e50c8cf81fa612dec1bd33d08c850d7..9cced9ee2b48f45c64890bc919e5f5d31be281f3 100644 --- a/src/plugins/inout/operational_inputs.ml +++ b/src/plugins/inout/operational_inputs.ml @@ -606,7 +606,7 @@ module Callwise = struct merge_call_in_local_table call_site table inout in match call_type with - | `Builtin {Value_types.c_from = Some (froms,sure_out) } -> + | `Builtin (Some (froms,sure_out)) -> let in_, out_ = extract_inout_from_froms froms in let inout = { over_inputs_if_termination = in_; @@ -624,7 +624,7 @@ module Callwise = struct | `Spec spec -> let inout =compute_using_given_spec_state state spec current_function in merge_inout inout - | `Builtin { Value_types.c_from = None } -> + | `Builtin None -> let inout = compute_using_prototype_state state current_function in merge_inout inout diff --git a/src/plugins/metrics/metrics_base.ml b/src/plugins/metrics/metrics_base.ml index ca172c1db4a5526ba842ee1dbedcc1782d57307b..a29fdada1c39483d090761c6af07185d31df7d24 100644 --- a/src/plugins/metrics/metrics_base.ml +++ b/src/plugins/metrics/metrics_base.ml @@ -365,7 +365,7 @@ let get_filename fdef = ;; let consider_function ~libc vinfo = - not (!Db.Value.mem_builtin vinfo.vname + not (Eva.Builtins.is_builtin vinfo.vname || Ast_info.is_frama_c_builtin vinfo.vname || Cil_builtins.is_unused_builtin vinfo ) && (libc || not (Cil.is_in_libc vinfo.vattr)) diff --git a/src/plugins/value/Eva.mli b/src/plugins/value/Eva.mli index fc8b622ad39830cb5a8912b7908f011d198ffb76..686e7ab24143eb3e8109230b809c2b36ef7955b8 100644 --- a/src/plugins/value/Eva.mli +++ b/src/plugins/value/Eva.mli @@ -101,3 +101,34 @@ module Eva_annotations: sig val add_subdivision_annot : emitter:Emitter.t -> loc:Cil_types.location -> Cil_types.stmt -> int -> unit end + +(** Analysis builtins for the cvalue domain, more efficient than the analysis + of the C functions. See {builtins.mli} for more details. *) +module Builtins: sig + open Cil_types + + exception Invalid_nb_of_args of int + exception Outside_builtin_possibilities + + type builtin_type = unit -> typ * typ list + type cacheable = Cacheable | NoCache | NoCacheCallers + + type full_result = { + c_values: (Cvalue.V.t option * Cvalue.Model.t) list; + c_clobbered: Base.SetLattice.t; + c_from: (Function_Froms.froms * Locations.Zone.t) option; + } + + type call_result = + | States of Cvalue.Model.t list + | Result of Cvalue.V.t list + | Full of full_result + + type builtin = Cvalue.Model.t -> (exp * Cvalue.V.t) list -> call_result + + val register_builtin: + string -> ?replace:string -> ?typ:builtin_type -> cacheable -> + builtin -> unit + + val is_builtin: string -> bool +end diff --git a/src/plugins/value/domains/cvalue/builtins.ml b/src/plugins/value/domains/cvalue/builtins.ml index f21513bf138ab55f4549da24607745400b5a7e68..5f571b6d2de43b264e2da334157e750bf5227ea9 100644 --- a/src/plugins/value/domains/cvalue/builtins.ml +++ b/src/plugins/value/domains/cvalue/builtins.ml @@ -21,14 +21,25 @@ (**************************************************************************) open Cil_types -open Cvalue exception Invalid_nb_of_args of int +exception Outside_builtin_possibilities -(* 'Always' means the builtin will always be used to replace a function - with its name. 'OnAuto' means that the function will be replaced only - if -eva-builtins-auto is set. *) -type use_builtin = Always | OnAuto +type builtin_type = unit -> typ * typ list +type cacheable = Eval.cacheable = Cacheable | NoCache | NoCacheCallers + +type full_result = { + c_values: (Cvalue.V.t option * Cvalue.Model.t) list; + c_clobbered: Base.SetLattice.t; + c_from: (Function_Froms.froms * Locations.Zone.t) option; +} + +type call_result = + | States of Cvalue.Model.t list + | Result of Cvalue.V.t list + | Full of full_result + +type builtin = Cvalue.Model.t -> (exp * Cvalue.V.t) list -> call_result (* Table of all registered builtins; filled by [register_builtin] calls. *) let table = Hashtbl.create 17 @@ -44,33 +55,28 @@ end (** Set of functions overridden by a builtin. *) module BuiltinsOverride = State_builder.Set_ref (Kernel_function.Set) (Info) -let register_builtin name ?replace ?typ f = - Hashtbl.replace table name (f, typ, None, Always); +let register_builtin name ?replace ?typ cacheable f = + Value_parameters.register_builtin name; + let builtin = (name, f, cacheable, typ) in + Hashtbl.replace table name builtin; match replace with | None -> () - | Some fname -> Hashtbl.replace table fname (f, typ, Some name, OnAuto) - -let () = Db.Value.register_builtin := register_builtin - -(* The functions in _builtin must only return the 'Always' builtins *) - -let registered_builtins () = - let l = - Hashtbl.fold - (fun name (f, _, _, u) acc -> if u = Always then (name, f) :: acc else acc) - table [] - in - List.sort (fun (name1, _) (name2, _) -> String.compare name1 name2) l + | Some fname -> Hashtbl.replace table fname builtin -let () = Db.Value.registered_builtins := registered_builtins +let is_builtin name = + try + let bname, _, _, _ = Hashtbl.find table name in + name = bname + with Not_found -> false let builtin_names_and_replacements () = let stand_alone, replacements = - Hashtbl.fold (fun name (_, _, replaced_by, _) (acc1, acc2) -> - match replaced_by with - | None -> name :: acc1, acc2 - | Some rep_by -> acc1, (name, rep_by) :: acc2 - ) table ([], []) + Hashtbl.fold + (fun name (builtin_name, _, _, _) (acc1, acc2) -> + if name = builtin_name + then name :: acc1, acc2 + else acc1, (name, builtin_name) :: acc2) + table ([], []) in List.sort String.compare stand_alone, List.sort (fun (name1, _) (name2, _) -> String.compare name1 name2) replacements @@ -104,13 +110,9 @@ let () = raise Cmdline.Exit end) -let mem_builtin name = - try - let _, _, _, u = Hashtbl.find table name in - u = Always - with Not_found -> false - -let () = Db.Value.mem_builtin := mem_builtin +(* -------------------------------------------------------------------------- *) +(* --- Prepare builtins for an analysis --- *) +(* -------------------------------------------------------------------------- *) (* Returns the specification of a builtin, used to evaluate preconditions and to transfer the states of other domains. *) @@ -155,7 +157,7 @@ let warn_builtin_override kf source bname = "function %s: definition will be overridden by %s" fname (if fname = bname then "its builtin" else "builtin " ^ bname) -let prepare_builtin kf builtin_name builtin expected_typ = +let prepare_builtin kf (name, builtin, cacheable, expected_typ) = let source = fst (Kernel_function.get_location kf) in if inconsistent_builtin_typ kf expected_typ then @@ -163,7 +165,7 @@ let prepare_builtin kf builtin_name builtin expected_typ = ~wkey:Value_parameters.wkey_builtins_override "The builtin %s will not be used for function %a of incompatible type.@ \ (got: %a)." - builtin_name Kernel_function.pretty kf + name Kernel_function.pretty kf Printer.pp_typ (Kernel_function.get_type kf) else match find_builtin_specification kf with @@ -174,9 +176,9 @@ let prepare_builtin kf builtin_name builtin expected_typ = specification is not available." Kernel_function.pretty kf | Some spec -> - warn_builtin_override kf source builtin_name; + warn_builtin_override kf source name; BuiltinsOverride.add kf; - Hashtbl.replace builtins_table kf (builtin_name, builtin, spec) + Hashtbl.replace builtins_table kf (name, builtin, cacheable, spec) let prepare_builtins () = BuiltinsOverride.clear (); @@ -184,35 +186,33 @@ let prepare_builtins () = let autobuiltins = Value_parameters.BuiltinsAuto.get () in (* Links kernel functions to the registered builtins. *) Hashtbl.iter - (fun name (f, typ, _bname, u) -> - if autobuiltins || u = Always + (fun name (bname, f, cacheable, typ) -> + if autobuiltins || name = bname then try let kf = Globals.Functions.find_by_name name in - prepare_builtin kf name f typ + prepare_builtin kf (name, f, cacheable, typ) with Not_found -> ()) table; (* Overrides builtins attribution according to the -eva-builtin option. *) Value_parameters.BuiltinsOverrides.iter (fun (kf, name) -> - let builtin_name = Option.get name in - let f, typ, _, _ = Hashtbl.find table builtin_name in - prepare_builtin kf builtin_name f typ) + prepare_builtin kf (Hashtbl.find table (Option.get name))) let find_builtin_override = Hashtbl.find_opt builtins_table -let is_builtin_overridden = +let is_builtin_overridden name = if not (BuiltinsOverride.is_computed ()) then prepare_builtins (); - BuiltinsOverride.mem + BuiltinsOverride.mem name (* -------------------------------------------------------------------------- *) -(* --- Returning a clobbered set --- *) +(* --- Applying a builtin --- *) (* -------------------------------------------------------------------------- *) let clobbered_set_from_ret state ret = let aux b _ acc = - match Model.find_base_or_default b state with + match Cvalue.Model.find_base_or_default b state with | `Top -> Base.SetLattice.top | `Bottom -> acc | `Value m -> @@ -220,71 +220,70 @@ let clobbered_set_from_ret state ret = Base.SetLattice.(join (inject_singleton b) acc) else acc in - try V.fold_topset_ok aux ret Base.SetLattice.bottom + try Cvalue.V.fold_topset_ok aux ret Base.SetLattice.bottom with Abstract_interp.Error_Top -> Base.SetLattice.top -(* -------------------------------------------------------------------------- *) -(* --- Applying a builtin --- *) -(* -------------------------------------------------------------------------- *) - type call = (Precise_locs.precise_location, Cvalue.V.t) Eval.call type result = Cvalue.Model.t * Locals_scoping.clobbered_set -type builtin = Db.Value.builtin open Eval -let unbottomize = function - | `Bottom -> Cvalue.V.bottom - | `Value v -> v - -let offsetmap_of_formals state arguments rest = - let compute expr assigned = - let offsm = Cvalue_offsetmap.offsetmap_of_assignment state expr assigned in - let value = unbottomize (Eval.value_assigned assigned) in - expr, value, offsm +let compute_arguments arguments rest = + let compute assigned = + match Eval.value_assigned assigned with + | `Bottom -> Cvalue.V.bottom + | `Value v -> v in - let treat_one_formal arg = compute arg.concrete arg.avalue in - let treat_one_rest (exp, v) = compute exp v in - let list = List.map treat_one_formal arguments in - let rest = List.map treat_one_rest rest in + let list = List.map (fun arg -> arg.concrete, compute arg.avalue) arguments in + let rest = List.map (fun (exp, v) -> exp, compute v) rest in list @ rest -let compute_builtin name builtin state actuals = - try builtin state actuals +let process_result call state call_result = + let clob = Locals_scoping.bottom () in + let bind_result state return = + match return, call.return with + | Some value, Some vi_ret -> + let b_ret = Base.of_varinfo vi_ret in + let offsm = Eval_op.offsetmap_of_v ~typ:vi_ret.vtype value in + Cvalue.Model.add_base b_ret offsm state, clob + | _, _ -> state, clob (* TODO: error? *) + in + match call_result with + | States states -> List.rev_map (fun s -> s, clob) states + | Result values -> List.rev_map (fun v -> bind_result state (Some v)) values + | Full result -> + Locals_scoping.remember_bases_with_locals clob result.c_clobbered; + let process_one_return acc (return, state) = + if Cvalue.Model.is_reachable state + then bind_result state return :: acc + else acc + in + List.fold_left process_one_return [] result.c_values + +let apply_builtin (builtin:builtin) call ~pre ~post = + let arguments = compute_arguments call.arguments call.rest in + try + let call_result = builtin pre arguments in + let call_stack = Value_util.call_stack () in + let froms = + match call_result with + | Full result -> `Builtin result.c_from + | States _ -> `Builtin None + | Result _ -> `Spec (Annotations.funspec call.kf) + in + Db.Value.Call_Type_Value_Callbacks.apply (froms, pre, call_stack); + process_result call post call_result with | Invalid_nb_of_args n -> Value_parameters.error ~current:true - "Invalid number of arguments for builtin %s: %d expected, %d found" - name n (List.length actuals); + "Invalid number of arguments for builtin %a: %d expected, %d found" + Kernel_function.pretty call.kf n (List.length arguments); raise Db.Value.Aborted - | Db.Value.Outside_builtin_possibilities -> + | Outside_builtin_possibilities -> Value_parameters.warning ~once:true ~current:true - "Call to builtin %s failed, aborting." name; + "Call to builtin %a failed, aborting." Kernel_function.pretty call.kf; raise Db.Value.Aborted -let apply_builtin builtin call state = - let name = Kernel_function.get_name call.kf in - let actuals = offsetmap_of_formals state call.arguments call.rest in - let res = compute_builtin name builtin state actuals in - let call_stack = Value_util.call_stack () in - Db.Value.Call_Type_Value_Callbacks.apply (`Builtin res, state, call_stack); - let clob = Locals_scoping.bottom () in - Locals_scoping.remember_bases_with_locals clob res.Value_types.c_clobbered; - let process_one_return acc (ret, post_state) = - if Cvalue.Model.is_reachable post_state then - let state = - match ret, call.return with - | Some offsm_ret, Some vi_ret -> - let b_ret = Base.of_varinfo vi_ret in - Cvalue.Model.add_base b_ret offsm_ret post_state - | _, _ -> post_state - in - (state, clob) :: acc - else - acc - in - let list = List.fold_left process_one_return [] res.Value_types.c_values in - list, res.Value_types.c_cacheable (* Local Variables: diff --git a/src/plugins/value/domains/cvalue/builtins.mli b/src/plugins/value/domains/cvalue/builtins.mli index d4c8e217ac82dc4059ba3e4b32208fb4cf8972d5..353be4b52e81e4b3ef4ee8c9b868a391d0f7f2e8 100644 --- a/src/plugins/value/domains/cvalue/builtins.mli +++ b/src/plugins/value/domains/cvalue/builtins.mli @@ -20,20 +20,64 @@ (* *) (**************************************************************************) -(** Value analysis builtin shipped with Frama-C, more efficient than their - equivalent in C *) +(** 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.mli} 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 f] registers the ocaml function [f] +(** [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 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:Db.Value.builtin_type -> Db.Value.builtin -> unit + string -> ?replace:string -> ?typ:builtin_type -> cacheable -> builtin -> unit + +(** Has a builtin been registered with the given name? *) +val is_builtin: string -> bool (** Prepares the builtins to be used for an analysis. Must be called at the beginning of each Eva analysis. Warns about builtins of incompatible types, @@ -41,28 +85,27 @@ val register_builtin: definitions. *) val prepare_builtins: unit -> unit +(** Is a given function replaced by a builtin? *) +val is_builtin_overridden: kernel_function -> bool + (** [clobbered_set_from_ret state ret] can be used for functions that return a pointer to where they have written some data. It returns all the bases of [ret] whose contents may contain local variables. *) val clobbered_set_from_ret: Cvalue.Model.t -> Cvalue.V.t -> Base.SetLattice.t -type builtin type call = (Precise_locs.precise_location, Cvalue.V.t) Eval.call -type result = Cvalue.Model.t * Locals_scoping.clobbered_set - -(** Is a given function replaced by a builtin? *) -val is_builtin_overridden: Cil_types.kernel_function -> bool +type result = Cvalue_domain.State.t (** Returns the cvalue builtin for a function, if any. Also returns the name of the builtin and the specification of the function; the preconditions must be evaluated along with the builtin. [prepare_builtins] should have been called before using this function. *) val find_builtin_override: - Cil_types.kernel_function -> (string * builtin * Cil_types.funspec) option + kernel_function -> (string * builtin * cacheable * funspec) option (* Applies a cvalue builtin for the given call, in the given cvalue state. *) val apply_builtin: - builtin -> call -> Cvalue.Model.t -> result list * Value_types.cacheable + builtin -> call -> pre:Cvalue.Model.t -> post:Cvalue.Model.t -> result list (* diff --git a/src/plugins/value/domains/cvalue/builtins_float.ml b/src/plugins/value/domains/cvalue/builtins_float.ml index 8194285d1c601ae0bb9a120e7a87a233dbd7a427..f950c308568da01b587e7d9c0a39c9b333214cc5 100644 --- a/src/plugins/value/domains/cvalue/builtins_float.ml +++ b/src/plugins/value/domains/cvalue/builtins_float.ml @@ -22,11 +22,6 @@ open Cvalue -let wrap_fk r = function - | Cil_types.FFloat -> Eval_op.wrap_float r - | Cil_types.FDouble -> Eval_op.wrap_double r - | _ -> assert false - let restrict_float ~assume_finite fkind value = let truth = Cvalue_forward.assume_not_nan ~assume_finite fkind value in match truth with @@ -44,35 +39,30 @@ let remove_special_float fk value = | "non-finite" -> restrict_float ~assume_finite:true fk value | _ -> assert false -let arity2 fk caml_fun state actuals = +let arity2 fk caml_fun _state actuals = match actuals with - | [_, arg1, _; _, arg2, _] -> - begin - let r = - try - let i1 = Cvalue.V.project_ival arg1 in - let f1 = Ival.project_float i1 in - let i2 = Cvalue.V.project_ival arg2 in - let f2 = Ival.project_float i2 in - let f' = Cvalue.V.inject_float (caml_fun (Fval.kind fk) f1 f2) in - remove_special_float fk f' - with Cvalue.V.Not_based_on_null -> - Cvalue.V.topify_arith_origin (V.join arg1 arg2) - in - { Value_types.c_values = - if V.is_bottom r then [] - else [wrap_fk r fk, state ]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; } - end + | [_, arg1; _, arg2] -> + let r = + try + let i1 = Cvalue.V.project_ival arg1 in + let f1 = Ival.project_float i1 in + let i2 = Cvalue.V.project_ival arg2 in + let f2 = Ival.project_float i2 in + let f' = Cvalue.V.inject_float (caml_fun (Fval.kind fk) f1 f2) in + remove_special_float fk f' + with Cvalue.V.Not_based_on_null -> + Cvalue.V.topify_arith_origin (V.join arg1 arg2) + in + let result = if V.is_bottom r then [] else [r] in + Builtins.Result result | _ -> raise (Builtins.Invalid_nb_of_args 2) let register_arity2 c_name fk f = let name = "Frama_C_" ^ c_name in + let replace = c_name in let t = Cil_types.TFloat (fk, []) in let typ () = t, [t; t] in - Builtins.register_builtin name ~replace:c_name ~typ (arity2 fk f) + Builtins.register_builtin name ~replace ~typ Cacheable (arity2 fk f) let () = let open Fval in @@ -83,39 +73,35 @@ let () = register_arity2 "fmod" Cil_types.FDouble fmod; register_arity2 "fmodf" Cil_types.FFloat fmod -let arity1 name fk caml_fun state actuals = +let arity1 name fk caml_fun _state actuals = match actuals with - | [_, arg, _] -> begin - let r = - try - let i = Cvalue.V.project_ival arg in - let f = Ival.project_float i in - let f' = Cvalue.V.inject_float (caml_fun (Fval.kind fk) f) in - remove_special_float fk f' - with - | Cvalue.V.Not_based_on_null -> - if Cvalue.V.is_bottom arg then begin - V.bottom - end else begin - Value_parameters.result ~once:true ~current:true - "function %s applied to address" name; - Cvalue.V.topify_arith_origin arg - end - in - { Value_types.c_values = - if V.is_bottom r then [] - else [wrap_fk r fk, state ]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; } - end + | [_, arg] -> + let r = + try + let i = Cvalue.V.project_ival arg in + let f = Ival.project_float i in + let f' = Cvalue.V.inject_float (caml_fun (Fval.kind fk) f) in + remove_special_float fk f' + with + | Cvalue.V.Not_based_on_null -> + if Cvalue.V.is_bottom arg then begin + V.bottom + end else begin + Value_parameters.result ~once:true ~current:true + "function %s applied to address" name; + Cvalue.V.topify_arith_origin arg + end + in + let result = if V.is_bottom r then [] else [r] in + Builtins.Result result | _ -> raise (Builtins.Invalid_nb_of_args 1) let register_arity1 c_name fk f = let name = "Frama_C_" ^ c_name in + let replace = c_name in let t = Cil_types.TFloat (fk, []) in let typ () = t, [t] in - Builtins.register_builtin name ~replace:c_name ~typ (arity1 name fk f) + Builtins.register_builtin name ~replace ~typ Cacheable (arity1 name fk f) let () = let open Fval in diff --git a/src/plugins/value/domains/cvalue/builtins_malloc.ml b/src/plugins/value/domains/cvalue/builtins_malloc.ml index 60a35cc4c72b208110f8a378f6f869508b15a22c..177c7f980d55a010df054ee50392ffaa2b057dd8 100644 --- a/src/plugins/value/domains/cvalue/builtins_malloc.ml +++ b/src/plugins/value/domains/cvalue/builtins_malloc.ml @@ -301,10 +301,10 @@ let add_zeroes = add_v (V_Or_Uninitialized.initialized Cvalue.V.singleton_zero) let wrap_fallible_alloc ?returns_null ret orig_state state_after_alloc = let default_returns_null = Value_parameters.AllocReturnsNull.get () in let returns_null = Option.value ~default:default_returns_null returns_null in - let success = Eval_op.wrap_ptr ret, state_after_alloc in + let success = Some ret, state_after_alloc in if returns_null then - let failure = Eval_op.wrap_ptr Cvalue.V.singleton_zero, orig_state in + let failure = Some Cvalue.V.singleton_zero, orig_state in [ success ; failure ] else [ success ] @@ -485,7 +485,7 @@ let choose_base_allocation () = let register_malloc ?replace name ?returns_null prefix region = let builtin state args = let size = match args with - | [ _, size, _ ] -> size + | [ _, size ] -> size | _ -> raise (Builtins.Invalid_nb_of_args 1) in let allocate_base = choose_base_allocation () in @@ -493,14 +493,12 @@ let register_malloc ?replace name ?returns_null prefix region = let new_state = add_uninitialized state new_base max_alloc in let ret = V.inject new_base Ival.zero in let c_values = wrap_fallible_alloc ?returns_null ret state new_state in - { Value_types.c_values = c_values ; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.NoCacheCallers; - c_from = None; } + let c_clobbered = Base.SetLattice.bottom in + Builtins.Full { c_values; c_clobbered; c_from = None; } in let name = "Frama_C_" ^ name in let typ () = Cil.voidPtrType, [Cil.theMachine.Cil.typeOfSizeOf] in - Builtins.register_builtin ?replace name builtin ~typ + Builtins.register_builtin ?replace name NoCacheCallers builtin ~typ let () = register_malloc ~replace:"malloc" "malloc" "malloc" Base.Malloc; @@ -526,7 +524,7 @@ let alloc_size_ok intended_size = let calloc_builtin state args = let nmemb, sizev = match args with - | [(_, nmemb, _); (_, size, _)] -> nmemb, size + | [(_, nmemb); (_, size)] -> nmemb, size | _ -> raise (Builtins.Invalid_nb_of_args 2) in let size = Cvalue.V.mul nmemb sizev in @@ -536,7 +534,7 @@ let calloc_builtin state args = "calloc out of bounds: assert(nmemb * size <= SIZE_MAX)"; let c_values = if size_ok = Alarmset.False (* size always overflows *) - then [Eval_op.wrap_ptr Cvalue.V.singleton_zero, state] + then [Some Cvalue.V.singleton_zero, state] else let allocate_base = choose_base_allocation () in let base, max_valid = allocate_base Base.Malloc "calloc" size state in @@ -547,18 +545,17 @@ let calloc_builtin state args = let ret = V.inject base Ival.zero in wrap_fallible_alloc ?returns_null ret state new_state in - { Value_types.c_values; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.NoCacheCallers; - c_from = None; } + let c_clobbered = Base.SetLattice.bottom in + Builtins.Full { c_values; c_clobbered; c_from = None; } let () = let name = "Frama_C_calloc" in + let replace = "calloc" in let typ () = let sizeof_typ = Cil.theMachine.Cil.typeOfSizeOf in Cil.voidPtrType, [ sizeof_typ; sizeof_typ ] in - Builtins.register_builtin ~replace:"calloc" name calloc_builtin ~typ + Builtins.register_builtin ~replace name NoCacheCallers calloc_builtin ~typ (* ---------------------------------- Free ---------------------------------- *) @@ -650,44 +647,37 @@ let free_aux state ~strong bases_to_remove = (* Builtin for [free] function *) let frama_c_free state actuals = match actuals with - | [ _, arg, _ ] -> + | [ _, arg ] -> let bases_to_remove, card_to_remove, _null = resolve_bases_to_free arg in + let c_clobbered = Base.SetLattice.bottom in if card_to_remove = 0 then - { Value_types.c_values = []; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; } + Builtins.Full { c_values = []; c_clobbered; c_from = None; } else let strong = card_to_remove <= 1 in let state, changed = free_aux state ~strong bases_to_remove in - { Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = Some changed; - c_cacheable = Value_types.Cacheable; - } + let c_values = [None, state] in + Builtins.Full { c_values; c_clobbered; c_from = Some changed; } | _ -> raise (Builtins.Invalid_nb_of_args 1) let () = - Builtins.register_builtin ~replace:"free" "Frama_C_free" frama_c_free - ~typ:(fun () -> (Cil.voidType, [Cil.voidPtrType])) + Builtins.register_builtin ~replace:"free" "Frama_C_free" Cacheable + frama_c_free ~typ:(fun () -> (Cil.voidType, [Cil.voidPtrType])) (* built-in for [__fc_vla_free] function. By construction, VLA should always be mapped to a single base. *) let frama_c_vla_free state actuals = match actuals with - | [ _, arg, _] -> + | [ _, arg ] -> let bases_to_remove, _card_to_remove, _null = resolve_bases_to_free arg in let state, changed = free_aux state ~strong:true bases_to_remove in - { Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = Some changed; - c_cacheable = Value_types.Cacheable; - } + let c_values = [None, state] in + let c_clobbered = Base.SetLattice.bottom in + Builtins.Full { c_values; c_clobbered; c_from = Some changed; } | _ -> raise (Builtins.Invalid_nb_of_args 1) let () = Builtins.register_builtin - ~replace:"__fc_vla_free" "Frama_C_vla_free" frama_c_vla_free + ~replace:"__fc_vla_free" "Frama_C_vla_free" Cacheable frama_c_vla_free ~typ:(fun () -> (Cil.voidType, [Cil.voidPtrType])) let free_automatic_bases stack state = @@ -830,7 +820,7 @@ let choose_bases_reallocation () = let realloc_builtin state args = let ptr, size = match args with - | [ (_, ptr, _); (_, size, _) ] -> ptr, size + | [ (_, ptr); (_, size) ] -> ptr, size | _ -> raise (Builtins.Invalid_nb_of_args 2) in let bases, card_ok, null = resolve_bases_to_free ptr in @@ -843,20 +833,17 @@ let realloc_builtin state args = (* free old bases. *) let new_state, changed = free_aux new_state ~strong bases in let c_values = wrap_fallible_alloc ret state new_state in - { Value_types.c_values; - c_clobbered = Builtins.clobbered_set_from_ret new_state ret; - c_cacheable = Value_types.NoCacheCallers; - c_from = Some changed; } + let c_clobbered = Builtins.clobbered_set_from_ret new_state ret in + Builtins.Full { c_values; c_clobbered; c_from = Some changed; } else (* Invalid call. *) - { Value_types.c_values = [] ; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.NoCacheCallers; - c_from = None; } + let c_clobbered = Base.SetLattice.bottom in + Builtins.Full { c_values = []; c_clobbered; c_from = None; } let () = let name = "Frama_C_realloc" in + let replace = "realloc" in let typ () = Cil.(voidPtrType, [voidPtrType; theMachine.typeOfSizeOf]) in - Builtins.register_builtin ~replace:"realloc" name realloc_builtin ~typ + Builtins.register_builtin ~replace name NoCacheCallers realloc_builtin ~typ (* ----------------------------- Leak detection ----------------------------- *) @@ -892,14 +879,12 @@ let check_leaked_malloced_bases state _ = Value_util.warning_once_current "memory leak detected for %a" Base.pretty base) alloced_bases; - { Value_types.c_values = [None,state] ; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.NoCacheCallers; - c_from = None; - } + let c_clobbered = Base.SetLattice.bottom in + Builtins.Full { c_values = [None,state]; c_clobbered; c_from = None; } let () = - Builtins.register_builtin "Frama_C_check_leak" check_leaked_malloced_bases + Builtins.register_builtin "Frama_C_check_leak" NoCacheCallers + check_leaked_malloced_bases (* diff --git a/src/plugins/value/domains/cvalue/builtins_memory.ml b/src/plugins/value/domains/cvalue/builtins_memory.ml index 8aea7089f54d77dd6bccf7b5a0d5ac052bf7e3f5..394404c9d03745fcb67a82cca5bf3be5eaf4351e 100644 --- a/src/plugins/value/domains/cvalue/builtins_memory.ml +++ b/src/plugins/value/domains/cvalue/builtins_memory.ml @@ -24,64 +24,38 @@ open Cil_types open Cvalue open Abstract_interp open Locations -open Value_util -let register_builtin = Builtins.register_builtin +let register_builtin name ?replace builtin = + Builtins.register_builtin name ?replace Cacheable builtin let dkey = Value_parameters.register_category "imprecision" -exception Found_misaligned_base - -let frama_C_is_base_aligned state actuals = - try begin - match actuals with - | [_,x,_; _,y,_] -> - let i = Cvalue.V.project_ival y in - begin match Ival.project_small_set i with - | Some si -> - Location_Bytes.fold_i - (fun b _o () -> - List.iter - (fun int -> - if not (Base.is_aligned_by b int) - then raise Found_misaligned_base) - si) +let frama_C_is_base_aligned _state = function + | [_, x; _, y] -> + let result = + match Ival.project_small_set (Cvalue.V.project_ival y) with + | Some si -> + let aligned = + Location_Bytes.for_all + (fun b _o -> List.for_all (Base.is_aligned_by b) si) x - (); - { Value_types.c_values = - [ Eval_op.wrap_int Cvalue.V.singleton_one, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } - | None -> raise Found_misaligned_base - end - | _ -> raise (Builtins.Invalid_nb_of_args 2) - end - with - | Found_misaligned_base - | Not_found (* from project_ival *) - | Abstract_interp.Error_Top (* from fold_i *) -> - { Value_types.c_values = [Eval_op.wrap_int Cvalue.V.zero_or_one, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } + in + if aligned then Cvalue.V.singleton_one else Cvalue.V.zero_or_one + | None + | exception Cvalue.V.Not_based_on_null -> Cvalue.V.zero_or_one + in + Builtins.Result [result] + | _ -> raise (Builtins.Invalid_nb_of_args 2) let () = register_builtin "Frama_C_is_base_aligned" frama_C_is_base_aligned -let frama_c_offset state actuals = - match actuals with - | [_,x,_] -> - let value = +let frama_c_offset _state = function + | [_, x] -> + let result = try - let offsets = - Location_Bytes.fold_i - (fun _b o a -> Ival.join a o) - x - Ival.bottom - in + let acc = Ival.bottom in + let offsets = Location_Bytes.fold_i (fun _b -> Ival.join) x acc in Cvalue.V.inject_ival offsets with Abstract_interp.Error_Top -> Value_parameters.error ~current:true @@ -89,11 +63,7 @@ let frama_c_offset state actuals = guaranteed to be an address"; Cvalue.V.top_int in - { Value_types.c_values = [Eval_op.wrap_size_t value, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } + Builtins.Result [result] | _ -> raise (Builtins.Invalid_nb_of_args 1) let () = register_builtin "Frama_C_offset" frama_c_offset @@ -130,7 +100,7 @@ let deps_nth_arg n = let frama_c_memcpy state actuals = - let compute (_exp_dst,dst_bytes,_) (_exp_src,src_bytes,_) (_exp_size,size,_) = + let compute (_exp_dst,dst_bytes) (_exp_src,src_bytes) (_exp_size,size) = let plevel = Value_parameters.ArrayPrecisionLevel.get() in let size = try Cvalue.V.project_ival size @@ -300,15 +270,15 @@ let frama_c_memcpy state actuals = if Model.is_reachable new_state then (* Copy at least partially succeeded (with perhaps an alarm for some of the sizes *) - { Value_types.c_values = [Eval_op.wrap_ptr dst_bytes, new_state]; - c_clobbered = Builtins.clobbered_set_from_ret new_state dst_bytes; - c_from = Some(c_from, sure_zone); - c_cacheable = Value_types.Cacheable } + Builtins.Full + { Builtins.c_values = [Some dst_bytes, new_state]; + c_clobbered = Builtins.clobbered_set_from_ret new_state dst_bytes; + c_from = Some (c_from, sure_zone); } else - { Value_types.c_values = [ None, Cvalue.Model.bottom]; - c_clobbered = Base.SetLattice.bottom; - c_from = Some(c_from, sure_zone); - c_cacheable = Value_types.Cacheable } + Builtins.Full + { Builtins.c_values = [ None, Cvalue.Model.bottom]; + c_clobbered = Base.SetLattice.bottom; + c_from = Some (c_from, sure_zone); } in match actuals with | [dst; src; size] -> compute dst src size @@ -385,11 +355,10 @@ let frama_c_memset_imprecise state dst v size = let deps_return = deps_nth_arg 0 in { deps_table; deps_return } in - { Value_types.c_values = [Eval_op.wrap_ptr dst, new_state']; - c_clobbered = Base.SetLattice.bottom; - c_from = Some(c_from,sure_zone); - c_cacheable = Value_types.Cacheable; - } + Builtins.Full + { Builtins.c_values = [Some dst, new_state']; + c_clobbered = Base.SetLattice.bottom; + c_from = Some (c_from,sure_zone); } (* let () = register_builtin "Frama_C_memset" frama_c_memset_imprecise *) (* Type that describes why the 'precise memset' builtin may fail. *) @@ -603,11 +572,10 @@ let frama_c_memset_precise state dst v (exp_size, size) = Cvalue.Model.paste_offsetmap ~from:offsm ~dst_loc ~size:size_bits ~exact:true state in - { Value_types.c_values = [Eval_op.wrap_ptr dst, state']; - c_clobbered = Base.SetLattice.bottom; - c_from = Some (c_from,dst_zone); - c_cacheable = Value_types.Cacheable; - } + Builtins.Full + { Builtins.c_values = [Some dst, state']; + c_clobbered = Base.SetLattice.bottom; + c_from = Some (c_from,dst_zone); } with | Bit_utils.NoMatchingOffset -> raise (ImpreciseMemset SizeMismatch) | Base.Not_a_C_variable -> raise (ImpreciseMemset NoTypeForDest) @@ -619,7 +587,7 @@ let frama_c_memset_precise state dst v (exp_size, size) = let frama_c_memset state actuals = match actuals with - | [(_exp_dst, dst, _); (_, v, _); (exp_size, size, _)] -> + | [(_exp_dst, dst); (_, v); (exp_size, size)] -> begin (* Remove read-only destinations *) let dst = V.filter_base (fun b -> not (Base.is_read_only b)) dst in @@ -634,7 +602,7 @@ let frama_c_memset state actuals = with ImpreciseMemset reason -> Value_parameters.debug ~dkey ~current:true "Call to builtin precise_memset(%a) failed; %a%t" - pretty_actuals actuals pretty_imprecise_memset_reason reason + Value_util.pretty_actuals actuals pretty_imprecise_memset_reason reason Value_util.pp_callstack; frama_c_memset_imprecise state dst v size end @@ -642,53 +610,40 @@ let frama_c_memset state actuals = let () = register_builtin ~replace:"memset" "Frama_C_memset" frama_c_memset -let frama_c_interval_split state actuals = - try - begin match actuals with - | [_,lower,_; _,upper,_] -> +let frama_c_interval_split _state actuals = + match actuals with + | [_,lower; _,upper] -> + begin + try let upper = Ival.project_int (Cvalue.V.project_ival upper) in let lower = Ival.project_int (Cvalue.V.project_ival lower) in let i = ref lower in let r = ref [] in while (Int.le !i upper) do - r := (Eval_op.wrap_int (Cvalue.V.inject_int !i), state) :: !r; + r := Cvalue.V.inject_int !i :: !r; i := Int.succ !i; done; - { Value_types.c_values = !r; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } - | _ -> raise (Builtins.Invalid_nb_of_args 2) + Builtins.Result !r + with + | Cvalue.V.Not_based_on_null + | Ival.Not_Singleton_Int -> + Value_parameters.error + "Invalid call to Frama_C_interval_split%a" + Value_util.pretty_actuals actuals; + raise Db.Value.Aborted end - with - | Cvalue.V.Not_based_on_null - | Ival.Not_Singleton_Int -> - Value_parameters.error - "Invalid call to Frama_C_interval_split%a" pretty_actuals actuals; - raise Db.Value.Aborted + | _ -> raise (Builtins.Invalid_nb_of_args 2) let () = register_builtin "Frama_C_interval_split" frama_c_interval_split (* Transforms a garbled mix into Top_int. Let other values unchanged. Remark: this currently returns an int. Maybe we need multiple versions? *) -let frama_c_ungarble state actuals = - begin match actuals with - | [_,i,_] -> - let v = - try - ignore (V.project_ival i); - i - with V.Not_based_on_null -> - V.inject_ival Ival.top - in - { Value_types.c_values = [ Eval_op.wrap_int v, state ]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } - | _ -> raise (Builtins.Invalid_nb_of_args 1) - end +let frama_c_ungarble _state = function + | [_, i] -> + if Cvalue.V.is_imprecise i + then Builtins.Result [Cvalue.V.top_int] + else Builtins.Result [i] + | _ -> raise (Builtins.Invalid_nb_of_args 1) let () = register_builtin "Frama_C_ungarble" frama_c_ungarble diff --git a/src/plugins/value/domains/cvalue/builtins_misc.ml b/src/plugins/value/domains/cvalue/builtins_misc.ml index 7b2e54d5fa9e1bf50b927908f683065923cb20fe..c4112b70f8b004939b2a5bf9e0aa890e5c36e15d 100644 --- a/src/plugins/value/domains/cvalue/builtins_misc.ml +++ b/src/plugins/value/domains/cvalue/builtins_misc.ml @@ -29,28 +29,23 @@ let frama_C_assert state actuals = Cvalue.Model.bottom in match actuals with - | [arg_exp, arg, _arg_offsm] -> begin - let state = - if Cvalue.V.is_zero arg - then do_bottom () - else if Cvalue.V.contains_zero arg - then begin - let state = !Db.Value.reduce_by_cond state arg_exp true in - if Cvalue.Model.is_reachable state - then (warning_once_current "Frama_C_assert: unknown"; state) - else do_bottom () - end - else begin - warning_once_current "Frama_C_assert: true"; - state - end - in - { Value_types.c_values = [ None, state ] ; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.NoCache; - } - end + | [arg_exp, arg] -> + let state = + if Cvalue.V.is_zero arg + then do_bottom () + else if Cvalue.V.contains_zero arg + then begin + let state = !Db.Value.reduce_by_cond state arg_exp true in + if Cvalue.Model.is_reachable state + then (warning_once_current "Frama_C_assert: unknown"; state) + else do_bottom () + end + else begin + warning_once_current "Frama_C_assert: true"; + state + end + in + Builtins.States [ state ] | _ -> raise (Builtins.Invalid_nb_of_args 1) -let () = Builtins.register_builtin "Frama_C_assert" frama_C_assert +let () = Builtins.register_builtin "Frama_C_assert" NoCache frama_C_assert diff --git a/src/plugins/value/domains/cvalue/builtins_print_c.ml b/src/plugins/value/domains/cvalue/builtins_print_c.ml index 28c2180087e06c2dc1993768818a08933439bac1..88c3a58a4fa49b19efc2fdaf53c6c7b93bc92841 100644 --- a/src/plugins/value/domains/cvalue/builtins_print_c.ml +++ b/src/plugins/value/domains/cvalue/builtins_print_c.ml @@ -325,25 +325,17 @@ let pretty_state_as_c_assignments fmt state = let frama_c_dump_assert state _actuals = Value_parameters.result ~current:true "Frama_C_dump_assert_each called:@\n(%a)@\nEnd of Frama_C_dump_assert_each output" pretty_state_as_c_assert state; - { Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.NoCache; - } + Builtins.States [state] -let () = Builtins.register_builtin "Frama_C_dump_assert_each" frama_c_dump_assert +let () = Builtins.register_builtin "Frama_C_dump_assert_each" NoCache frama_c_dump_assert let frama_c_dump_assignments state _actuals = Value_parameters.result ~current:true "Frama_C_dump_assignment_each called:@\n%a@\nEnd of Frama_C_dump_assignment_each output" pretty_state_as_c_assignments state; - { Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.NoCache; - } + Builtins.States [state] let () = - Builtins.register_builtin "Frama_C_dump_assignments_each" frama_c_dump_assignments + Builtins.register_builtin "Frama_C_dump_assignments_each" NoCache frama_c_dump_assignments (* diff --git a/src/plugins/value/domains/cvalue/builtins_split.ml b/src/plugins/value/domains/cvalue/builtins_split.ml index eabc8ced8ace46ec1af4b4055c39b35995f15e26..bcc7c2e5a68bc717203ed39264932f5ba88bdfd5 100644 --- a/src/plugins/value/domains/cvalue/builtins_split.ml +++ b/src/plugins/value/domains/cvalue/builtins_split.ml @@ -25,54 +25,41 @@ open Cil_types open Abstract_interp open Cvalue +let register_builtin name builtin = + Builtins.register_builtin name Cacheable builtin + (** Enumeration *) (** Cardinal of an abstract value (-1 if not enumerable). Beware this builtin is not monotonic *) -let frama_c_cardinal state actuals = - match actuals with - | [_, v, _] -> begin - let nb = match Cvalue.V.cardinal v with - | None -> Cvalue.V.inject_int Integer.minus_one - | Some i -> Cvalue.V.inject_int i - in - { Value_types.c_values = [Eval_op.wrap_long_long nb, state]; - c_clobbered = Base.SetLattice.empty; - c_cacheable = Value_types.Cacheable; - c_from = None; - } - end - | _ -> - Kernel.abort ~current:true "Incorrect argument for Frama_C_cardinal" +let frama_c_cardinal _state = function + | [_, v] -> + let nb = match Cvalue.V.cardinal v with + | None -> Cvalue.V.inject_int Integer.minus_one + | Some i -> Cvalue.V.inject_int i + in + Builtins.Result [nb] + | _ -> Kernel.abort ~current:true "Incorrect argument for Frama_C_cardinal" -let () = - !Db.Value.register_builtin "Frama_C_abstract_cardinal" frama_c_cardinal +let () = register_builtin "Frama_C_abstract_cardinal" frama_c_cardinal (** Minimum or maximum of an integer abstract value, Top_int otherwise. Also not monotonic. *) -let frama_c_min_max f state actuals = - match actuals with - | [_, v, _] -> begin - let nb = - try - match f (Ival.min_and_max (V.project_ival v)) with - | None -> Cvalue.V.top_int - | Some i -> Cvalue.V.inject_int i - with V.Not_based_on_null -> Cvalue.V.top_int - in - { Value_types.c_values = [Eval_op.wrap_long_long nb, state]; - c_clobbered = Base.SetLattice.empty; - c_cacheable = Value_types.Cacheable; - c_from = None; - } - end - | _ -> - Kernel.abort ~current:true "Incorrect argument for Frama_C_min/max" +let frama_c_min_max f _state = function + | [_, v] -> + let nb = + try + match f (Ival.min_and_max (V.project_ival v)) with + | None -> Cvalue.V.top_int + | Some i -> Cvalue.V.inject_int i + with V.Not_based_on_null -> Cvalue.V.top_int + in + Builtins.Result [nb] + | _ -> Kernel.abort ~current:true "Incorrect argument for Frama_C_min/max" let () = - !Db.Value.register_builtin "Frama_C_abstract_min" (frama_c_min_max fst); - !Db.Value.register_builtin "Frama_C_abstract_max" (frama_c_min_max snd); -;; + register_builtin "Frama_C_abstract_min" (frama_c_min_max fst); + register_builtin "Frama_C_abstract_max" (frama_c_min_max snd) (** Splitting values *) @@ -186,44 +173,26 @@ let split_all ~warn lv state max_card = (* Auxiliary function, used to register a 'Frama_C_split' variant. Only the parsing and the error handling is shared; all the hard work is done by [f] *) -let aux_split f state actuals = - match actuals with - | [({ enode = (Lval lv | CastE (_, {enode = Lval lv}))}, _, _); - (_, card, _)] -> - begin +let aux_split f state = function + | [({ enode = (Lval lv | CastE (_, {enode = Lval lv}))}, _); (_, card)] -> + let states = try let max_card = Integer.to_int (Ival.project_int (V.project_ival_bottom card)) in - let states = f ~warn:true lv state max_card in - (* Add empty return *) - let states = List.map (fun state -> None, state) states in - { Value_types.c_values = states; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.Cacheable; - c_from = None; - } + f ~warn:true lv state max_card with V.Not_based_on_null | Ival.Not_Singleton_Int -> Value_parameters.warning ~current:true ~once:true "Cannot use non-constant split level %a" V.pretty card; - { Value_types.c_values = [(None, state)]; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.Cacheable; - c_from = None; - } - end + [state] + in + Builtins.States states | _ -> Value_parameters.warning ~current:true ~once:true "Cannot interpret split directive. Ignoring"; - { Value_types.c_values = [(None, state)]; - c_clobbered = Base.SetLattice.bottom; - c_cacheable = Value_types.Cacheable; - c_from = None; - } + Builtins.States [state] let () = - !Db.Value.register_builtin "Frama_C_builtin_split" (aux_split split_v) -let () = - !Db.Value.register_builtin "Frama_C_builtin_split_pointer" (aux_split split_pointer) -let () = - !Db.Value.register_builtin "Frama_C_builtin_split_all" (aux_split split_all) + register_builtin "Frama_C_builtin_split" (aux_split split_v); + register_builtin "Frama_C_builtin_split_pointer" (aux_split split_pointer); + register_builtin "Frama_C_builtin_split_all" (aux_split split_all) diff --git a/src/plugins/value/domains/cvalue/builtins_string.ml b/src/plugins/value/domains/cvalue/builtins_string.ml index d479ad1dca70ae6fde9c20075f2611ba92a99b79..97bae714d07b173d5a56aac6c858e699cef058da 100644 --- a/src/plugins/value/domains/cvalue/builtins_string.ml +++ b/src/plugins/value/domains/cvalue/builtins_string.ml @@ -376,54 +376,44 @@ let do_search ~search ~stop_at_0 ~typ ~length ?limit = fun state args -> let size = bits_size typ in let signed = signed_char typ in let str = List.nth args 0 in - let result, alarm = - try - let str, valid = reduce_by_validity ~size str in - let search = - if Ival.is_bottom search - then searched_char ~size ~signed (List.nth args 1) - else search - in - (* When searching exactly 0, the search naturally stops at 0. *) - let stop_at_0 = if Ival.is_zero search then false else stop_at_0 in - let interpret_limit n = - let cvalue = List.nth args n in - let limit = Ival.scale size (Cvalue.V.project_ival cvalue) in - Ival.(narrow positive_integers limit) - in - let limit = Option.map interpret_limit limit in - let kind = { search; stop_at_0; size; signed; limit } in - let result, alarm = search_char kind ~length state str in - result, alarm || not valid - with | Abstract_interp.Error_Top - | Cvalue.V.Not_based_on_null -> return_top ~length str, true - in - let wrapper = if length then Eval_op.wrap_size_t else Eval_op.wrap_ptr in - if Cvalue.V.is_bottom result then None, alarm else wrapper result, alarm + try + let str, valid = reduce_by_validity ~size str in + let search = + if Ival.is_bottom search + then searched_char ~size ~signed (List.nth args 1) + else search + in + (* When searching exactly 0, the search naturally stops at 0. *) + let stop_at_0 = if Ival.is_zero search then false else stop_at_0 in + let interpret_limit n = + let cvalue = List.nth args n in + let limit = Ival.scale size (Cvalue.V.project_ival cvalue) in + Ival.(narrow positive_integers limit) + in + let limit = Option.map interpret_limit limit in + let kind = { search; stop_at_0; size; signed; limit } in + let result, alarm = search_char kind ~length state str in + result, alarm || not valid + with | Abstract_interp.Error_Top + | Cvalue.V.Not_based_on_null -> return_top ~length str, true (* Applies the [builtin] built by [do_search]. *) let apply_builtin _name builtin = fun state args -> - let args = List.map (fun (_, v, _) -> v) args in + let args = List.map snd args in let result, _alarm = builtin state args in - let res_cvalue = match result with - | None -> None, Cvalue.Model.bottom - | Some _ -> result, state - in - { Value_types.c_values = [ res_cvalue ]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; } + let result = if Cvalue.V.is_bottom result then [] else [result] in + Builtins.Result result (* Builds, registers and exports a builtin for the C function [c_name]. *) let register_builtin c_name ~search ~stop_at_0 ~typ ~length ?limit = let name = "Frama_C_" ^ c_name in let f = do_search ~search ~stop_at_0 ~typ ~length ?limit in let builtin = apply_builtin name f in - Builtins.register_builtin name ~replace:c_name builtin; + Builtins.register_builtin name ~replace:c_name Cacheable builtin; f type str_builtin_sig = - Cvalue.Model.t -> Cvalue.V.t list -> Cvalue.V_Offsetmap.t option * bool + Cvalue.Model.t -> Cvalue.V.t list -> Cvalue.V.t * bool let frama_c_strlen_wrapper : str_builtin_sig = register_builtin "strlen" diff --git a/src/plugins/value/domains/cvalue/builtins_string.mli b/src/plugins/value/domains/cvalue/builtins_string.mli index feea3c2719fbae80a462531a671a89bc02fe46da..8883ed2efeb68bbf3d6863a2b0fe90997e20d3ea 100644 --- a/src/plugins/value/domains/cvalue/builtins_string.mli +++ b/src/plugins/value/domains/cvalue/builtins_string.mli @@ -21,10 +21,10 @@ (**************************************************************************) (** A builtin takes the state and a list of values for the arguments, and - returns the offsetmap of the return value (None if bottom), and a boolean - indicating the possibility of alarms. *) + returns the return value (which can be bottom), and a boolean indicating the + possibility of alarms. *) type str_builtin_sig = - Cvalue.Model.t -> Cvalue.V.t list -> Cvalue.V_Offsetmap.t option * bool + Cvalue.Model.t -> Cvalue.V.t list -> Cvalue.V.t * bool val frama_c_strlen_wrapper: str_builtin_sig val frama_c_wcslen_wrapper: str_builtin_sig diff --git a/src/plugins/value/domains/cvalue/builtins_watchpoint.ml b/src/plugins/value/domains/cvalue/builtins_watchpoint.ml index d7b0df933a864380c577ea0c1143bd2517255360..2ae3ce11c47ef2a8aaf2dcf3cc877a2fa7c8e34b 100644 --- a/src/plugins/value/domains/cvalue/builtins_watchpoint.ml +++ b/src/plugins/value/domains/cvalue/builtins_watchpoint.ml @@ -49,20 +49,20 @@ let new_watchpoint name_lv loc v n = let add_watch make_watch state actuals = match actuals with - | [(dst_e, dst, _); (_, size, _); (_, target_value, _); (_, number, _)] -> + | [(dst_e, dst); (_, size); (_, target_value); (_, number)] -> let size = try let size = Cvalue.V.project_ival size in Int.mul Int.eight (Ival.project_int size) with V.Not_based_on_null | Ival.Not_Singleton_Int -> - raise Db.Value.Outside_builtin_possibilities + raise Builtins.Outside_builtin_possibilities in let number = try let number = Cvalue.V.project_ival number in Ival.project_int number with V.Not_based_on_null | Ival.Not_Singleton_Int -> - raise Db.Value.Outside_builtin_possibilities + raise Builtins.Outside_builtin_possibilities in let loc_bits = Locations.loc_bytes_to_loc_bits dst in let loc = Locations.make_loc loc_bits (Int_Base.inject size) in @@ -76,10 +76,7 @@ let add_watch make_watch state actuals = then watch_table := (new_watchpoint dst_e loc target_w number) :: current; - { Value_types.c_values = [None, state]; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable } + Builtins.States [state] | _ -> raise (Builtins.Invalid_nb_of_args 4) let make_watch_value target_value = Value target_value @@ -90,13 +87,13 @@ let make_watch_cardinal target_value = Cardinal (Integer.to_int (Ival.project_int target_value)) with V.Not_based_on_null | Ival.Not_Singleton_Int | Z.Overflow (* from Integer.to_int *) -> - raise Db.Value.Outside_builtin_possibilities + raise Builtins.Outside_builtin_possibilities let () = - Builtins.register_builtin "Frama_C_watch_value" (add_watch make_watch_value) + Builtins.register_builtin "Frama_C_watch_value" Cacheable + (add_watch make_watch_value) let () = - Builtins.register_builtin - "Frama_C_watch_cardinal" + Builtins.register_builtin "Frama_C_watch_cardinal" Cacheable (add_watch make_watch_cardinal) let watch_hook (stmt, _callstack, states) = diff --git a/src/plugins/value/engine/compute_functions.ml b/src/plugins/value/engine/compute_functions.ml index b41b8cc4851e0eb6063887946f3b6d5dd4276c16..b622130d6e7c272654cfa450ebc47e2f5d7232d1 100644 --- a/src/plugins/value/engine/compute_functions.ml +++ b/src/plugins/value/engine/compute_functions.ml @@ -195,7 +195,7 @@ module Make (Abstract: Abstractions.Eva) = struct if Cil.is_in_libc vi.vattr then Library_functions.warn_unsupported_spec vi.vorig_name; Spec.compute_using_specification ~warn:true call_kinstr call spec state, - Value_types.Cacheable + Eval.Cacheable | `Def _fundec -> Db.Value.Call_Type_Value_Callbacks.apply (`Def, cvalue_state, call_stack); Computer.compute kf call_kinstr state @@ -221,7 +221,7 @@ module Make (Abstract: Abstractions.Eva) = struct let call_result = default () in let () = if not (!Db.Value.use_spec_instead_of_definition call.kf) - && call_result.Transfer.cacheable = Value_types.Cacheable + && call_result.Transfer.cacheable = Eval.Cacheable then let final_states = call_result.Transfer.states in MemExec.store_computed_call call.kf init_state args final_states @@ -251,7 +251,7 @@ module Make (Abstract: Abstractions.Eva) = struct Db.Value.Record_Value_Callbacks_New.apply (stack_with_call, Value_types.Reuse i); (* call can be cached since it was cached once *) - Transfer.{states; cacheable = Value_types.Cacheable; builtin=false} + Transfer.{states; cacheable = Cacheable; builtin=false} else default () @@ -275,7 +275,7 @@ module Make (Abstract: Abstractions.Eva) = struct let compute_call_or_builtin stmt call state = match Builtins.find_builtin_override call.kf with | None -> compute_and_cache_call stmt call state - | Some (name, builtin, spec) -> + | Some (name, builtin, cacheable, spec) -> Value_results.mark_kf_as_called call.kf; let kinstr = Kstmt stmt in let kf_name = Kernel_function.get_name call.kf in @@ -296,12 +296,13 @@ module Make (Abstract: Abstractions.Eva) = struct | `Bottom -> let cs = Value_util.call_stack () in Db.Value.Call_Type_Value_Callbacks.apply (`Spec spec, cvalue_state, cs); - let cacheable = Value_types.Cacheable in + let cacheable = Eval.Cacheable in Transfer.{states; cacheable; builtin=true} | `Value final_state -> let cvalue_call = get_cvalue_call call in - let cvalue_states, cacheable = - Builtins.apply_builtin builtin cvalue_call cvalue_state + let post = Abstract.Dom.get_cvalue_or_top final_state in + let cvalue_states = + Builtins.apply_builtin builtin cvalue_call ~pre:cvalue_state ~post in let insert cvalue_state = Abstract.Dom.set Cvalue_domain.State.key cvalue_state final_state diff --git a/src/plugins/value/engine/iterator.ml b/src/plugins/value/engine/iterator.ml index 8de2b35637636c8fb659923ce0c9cb30b2a33084..930c01942de58d96d47e0845553c4b4e9e249415 100644 --- a/src/plugins/value/engine/iterator.ml +++ b/src/plugins/value/engine/iterator.ml @@ -66,7 +66,7 @@ module Make_Dataflow let kf = AnalysisParam.kf let fundec = Kernel_function.get_definition kf - let cacheable = ref Value_types.Cacheable + let cacheable = ref Eval.Cacheable (* --- Plugin parameters --- *) @@ -273,9 +273,9 @@ module Make_Dataflow let result, call_cacheable = Transfer.call stmt dest callee args state in - if call_cacheable = Value_types.NoCacheCallers then + if call_cacheable = Eval.NoCacheCallers then (* Propagate info that the current call cannot be cached either *) - cacheable := Value_types.NoCacheCallers; + cacheable := Eval.NoCacheCallers; Bottom.list_of_bot result let transfer_instr (stmt : stmt) (instr : instr) : transfer_function = diff --git a/src/plugins/value/engine/iterator.mli b/src/plugins/value/engine/iterator.mli index d11bb54625270b285e09ce0e6fd051deb55caf7c..163796d15e9f89c74a07ebdc070d07dbd5e92352 100644 --- a/src/plugins/value/engine/iterator.mli +++ b/src/plugins/value/engine/iterator.mli @@ -46,7 +46,7 @@ module Computer val compute: kernel_function -> kinstr -> Abstract.Dom.t -> - Abstract.Dom.t list or_bottom * Value_types.cacheable + Abstract.Dom.t list or_bottom * Eval.cacheable end diff --git a/src/plugins/value/engine/transfer_stmt.ml b/src/plugins/value/engine/transfer_stmt.ml index 34ea9d023571c31f42450e6c07ff4c34d3e85d2b..040629aa88699be86afd064e0775d464398b4e34 100644 --- a/src/plugins/value/engine/transfer_stmt.ml +++ b/src/plugins/value/engine/transfer_stmt.ml @@ -32,7 +32,7 @@ module type S = sig val assume: state -> stmt -> exp -> bool -> state or_bottom val call: stmt -> lval option -> exp -> exp list -> state -> - state list or_bottom * Value_types.cacheable + state list or_bottom * Eval.cacheable val check_unspecified_sequence: stmt -> state -> (stmt * lval list * lval list * lval list * stmt ref list) list -> @@ -40,7 +40,7 @@ module type S = sig val enter_scope: kernel_function -> varinfo list -> state -> state type call_result = { states: state list or_bottom; - cacheable: Value_types.cacheable; + cacheable: Eval.cacheable; builtin: bool; } val compute_call_ref: @@ -290,7 +290,7 @@ module Make (Abstract: Abstractions.Eva) = struct type call_result = { states: state list or_bottom; - cacheable: Value_types.cacheable; + cacheable: cacheable; builtin: bool; } @@ -317,7 +317,7 @@ module Make (Abstract: Abstractions.Eva) = struct Domain.Store.register_initial_state (Value_util.call_stack ()) state; !compute_call_ref stmt call state | `Bottom -> - { states = `Bottom; cacheable = Value_types.Cacheable; builtin=false } + { states = `Bottom; cacheable = Cacheable; builtin=false } in cleanup (); res @@ -716,15 +716,8 @@ module Make (Abstract: Abstractions.Eva) = struct let cvalue_state = Domain.get_cvalue_or_top state in Db.Value.Call_Value_Callbacks.apply (cvalue_state, stack_with_call); Db.Value.merge_initial_state (Value_util.call_stack ()) cvalue_state; - let result = - { Value_types.c_values = [ None, cvalue_state] ; - c_clobbered = Base.SetLattice.bottom; - c_from = None; - c_cacheable = Value_types.Cacheable; - } - in Db.Value.Call_Type_Value_Callbacks.apply - (`Builtin result, cvalue_state, stack_with_call) + (`Builtin None, cvalue_state, stack_with_call) (* --------------------- Process the call statement ---------------------- *) @@ -737,7 +730,7 @@ module Make (Abstract: Abstractions.Eva) = struct Eval.eval_function_exp ~subdivnb funcexp ~args state in Alarmset.emit ki_call alarms; - let cacheable = ref Value_types.Cacheable in + let cacheable = ref Cacheable in let eval = functions >>- fun functions -> let current_kf = Value_util.current_kf () in @@ -757,8 +750,8 @@ module Make (Abstract: Abstractions.Eva) = struct (* Do the call. *) let c, states = do_one_call valuation stmt lval_option call state in (* If needed, propagate that callers cannot be cached. *) - if c = Value_types.NoCacheCallers then - cacheable := Value_types.NoCacheCallers; + if c = NoCacheCallers then + cacheable := NoCacheCallers; states in (* Process each possible function apart, and append the result list. *) diff --git a/src/plugins/value/engine/transfer_stmt.mli b/src/plugins/value/engine/transfer_stmt.mli index c280d047b07afa5c60902dadb7b1de0880d81d02..af7e5fa3e7383125abfbb9101e64a23d5b8f9baf 100644 --- a/src/plugins/value/engine/transfer_stmt.mli +++ b/src/plugins/value/engine/transfer_stmt.mli @@ -37,7 +37,7 @@ module type S = sig val call: stmt -> lval option -> exp -> exp list -> state -> - state list or_bottom * Value_types.cacheable + state list or_bottom * Eval.cacheable val check_unspecified_sequence: Cil_types.stmt -> @@ -50,7 +50,7 @@ module type S = sig type call_result = { states: state list or_bottom; - cacheable: Value_types.cacheable; + cacheable: Eval.cacheable; builtin: bool; } diff --git a/src/plugins/value/eval.ml b/src/plugins/value/eval.ml index 2702268a75fcfe979f8c8060fcf866574e88fc1c..35890fccc1ce014f27793899aa2029ee5cafe3ec 100644 --- a/src/plugins/value/eval.ml +++ b/src/plugins/value/eval.ml @@ -243,6 +243,8 @@ type ('loc, 'value) call = { recursive: bool; } +type cacheable = Cacheable | NoCache | NoCacheCallers + (* Local Variables: compile-command: "make -C ../../.." diff --git a/src/plugins/value/eval.mli b/src/plugins/value/eval.mli index 28301c46e79ddeb64d84603b5f97f584fc34ca63..31f6e332eca013eac6d003c7c202eb07c6038a6a 100644 --- a/src/plugins/value/eval.mli +++ b/src/plugins/value/eval.mli @@ -220,6 +220,15 @@ type ('loc, 'value) call = { recursive: bool; } +(* 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 *) + (* Local Variables: compile-command: "make -C ../../.." diff --git a/src/plugins/value/legacy/eval_op.ml b/src/plugins/value/legacy/eval_op.ml index 1cc14743aa781c7880a58f42a98e8842b606f937..8c754c4a2696134bef381ff1320dbe5d4caeeeb2 100644 --- a/src/plugins/value/legacy/eval_op.ml +++ b/src/plugins/value/legacy/eval_op.ml @@ -39,14 +39,6 @@ let offsetmap_of_loc location state = in Precise_locs.fold aux location `Bottom -let wrap_int i = Some (offsetmap_of_v ~typ:Cil.intType i) -let wrap_ptr p = Some (offsetmap_of_v ~typ:Cil.intPtrType p) -let wrap_double d = Some (offsetmap_of_v ~typ:Cil.doubleType d) -let wrap_float d = Some (offsetmap_of_v ~typ:Cil.floatType d) -let wrap_size_t i = - Some (offsetmap_of_v ~typ:(Cil.theMachine.Cil.typeOfSizeOf) i) -let wrap_long_long i = Some (offsetmap_of_v ~typ:Cil.longLongType i) - let v_uninit_of_offsetmap ~typ offsm = let size = Eval_typ.sizeof_lval_typ typ in match size with diff --git a/src/plugins/value/legacy/eval_op.mli b/src/plugins/value/legacy/eval_op.mli index 9e0e02ed42f34a5d75b917bae0189bfc1a0b6d28..ef8f6150c68314d7c18f5b8681755b651ac52d80 100644 --- a/src/plugins/value/legacy/eval_op.mli +++ b/src/plugins/value/legacy/eval_op.mli @@ -33,14 +33,6 @@ val offsetmap_of_v: typ:Cil_types.typ -> V.t -> V_Offsetmap.t val offsetmap_of_loc: Precise_locs.precise_location -> Model.t -> V_Offsetmap.t Eval.or_bottom -(** Specialization of the function above for standard types *) -val wrap_size_t: V.t -> V_Offsetmap.t option -val wrap_int: V.t -> V_Offsetmap.t option -val wrap_ptr: V.t -> V_Offsetmap.t option -val wrap_double: V.t -> V_Offsetmap.t option -val wrap_float: V.t -> V_Offsetmap.t option -val wrap_long_long: V.t -> V_Offsetmap.t option - val backward_comp_left_from_type: logic_type -> (bool -> Abstract_interp.Comp.t -> Cvalue.V.t -> Cvalue.V.t -> Cvalue.V.t) diff --git a/src/plugins/value/legacy/eval_terms.ml b/src/plugins/value/legacy/eval_terms.ml index 4a19ba063da93259551138260379081ece972574..5de658b98e54d52516fc62bf0f3e08d402e3bda4 100644 --- a/src/plugins/value/legacy/eval_terms.ml +++ b/src/plugins/value/legacy/eval_terms.ml @@ -568,28 +568,20 @@ let constraint_trange idx size_arr = (* Applies a cvalue [builtin] to the list of arguments [args_list] in the current state of [env]. Returns [v, alarms], where [v] is the resulting - cvalue, or [None] if the builtin leads to [bottom]. *) + cvalue, which can be bottom. *) let apply_logic_builtin builtin env args_list = (* the call below could in theory return Builtins.Invalid_nb_of_args, but logic typing constraints prevent that. *) - let res, alarms = builtin (env_current_state env) args_list in - match res with - | None -> None - | Some offsm -> - let v = Option.get (Cvalue.V_Offsetmap.single_interval_value offsm) in - let v = Cvalue.V_Or_Uninitialized.get_v v in - Some (v, alarms) + builtin (env_current_state env) args_list (* Never raises exceptions; instead, returns [-1,+oo] in case of alarms (most imprecise result possible for the logic strlen/wcslen predicates). *) let eval_logic_charlen wrapper env v ldeps = let eover = - match apply_logic_builtin wrapper env [v] with - | None -> Cvalue.V.bottom - | Some (v, alarms) -> - if alarms - then Cvalue.V.inject_ival (Ival.inject_range (Some Int.minus_one) None) - else v + let v, alarms = apply_logic_builtin wrapper env [v] in + if alarms && not (Cvalue.V.is_bottom v) + then Cvalue.V.inject_ival (Ival.inject_range (Some Int.minus_one) None) + else v in let eunder = under_from_over eover in (* the C strlen function has type size_t, but the logic strlen function has @@ -600,19 +592,16 @@ let eval_logic_charlen wrapper env v ldeps = (* Evaluates the logical predicates strchr/wcschr. *) let eval_logic_charchr builtin env s c ldeps_s ldeps_c = let eover = - match apply_logic_builtin builtin env [s; c] with - | None -> Cvalue.V.bottom - | Some (r, alarms) -> - if alarms - then Cvalue.V.zero_or_one - else - let ctrue = Cvalue.V.contains_non_zero r - and cfalse = Cvalue.V.contains_zero r in - match ctrue, cfalse with - | true, true -> Cvalue.V.zero_or_one - | true, false -> Cvalue.V.singleton_one - | false, true -> Cvalue.V.singleton_zero - | false, false -> assert false (* a logic alarm would have been raised*) + let v, alarms = apply_logic_builtin builtin env [s; c] in + if Cvalue.V.is_bottom v then v else + if alarms then Cvalue.V.zero_or_one else + let ctrue = Cvalue.V.contains_non_zero v + and cfalse = Cvalue.V.contains_zero v in + match ctrue, cfalse with + | true, true -> Cvalue.V.zero_or_one + | true, false -> Cvalue.V.singleton_one + | false, true -> Cvalue.V.singleton_zero + | false, false -> assert false (* a logic alarm would have been raised*) in let eunder = under_from_over eover in (* the C strchr function has type char*, but the logic strchr predicate has @@ -629,11 +618,12 @@ let eval_logic_memchr_off builtin env s c n = let n_pos = Cvalue.V.narrow positive pred_n in let eover = if Cvalue.V.is_bottom n_pos then minus_one else - match apply_logic_builtin builtin env [s.eover; c.eover; n_pos] with - | None -> pred_n - | Some (v, alarms) -> - if alarms then Cvalue.V.join pred_n v else - if Cvalue.V.equal n_pos pred_n then v else Cvalue.V.join minus_one v + let args = [s.eover; c.eover; n_pos] in + let v, alarms = apply_logic_builtin builtin env args in + if Cvalue.V.is_bottom v then pred_n else + if alarms then Cvalue.V.join pred_n v else + if Cvalue.V.equal n_pos pred_n then v else + Cvalue.V.join minus_one v in let ldeps = join_logic_deps s.ldeps (join_logic_deps c.ldeps n.ldeps) in { (einteger eover) with ldeps } @@ -1681,12 +1671,12 @@ let eval_valid_read_str ~wide env v = if wide then Builtins_string.frama_c_wcslen_wrapper else Builtins_string.frama_c_strlen_wrapper in - match apply_logic_builtin wrapper env [v] with - | None -> (* bottom state => string always invalid *) False - | Some (_res, alarms) -> - if alarms - then (* alarm => string possibly invalid *) Unknown - else (* no alarm => string always valid for reading *) True + let v, alarms = apply_logic_builtin wrapper env [v] in + if Cvalue.V.is_bottom v + then False (* bottom state => string always invalid *) + else if alarms + then Unknown (* alarm => string possibly invalid *) + else True (* no alarm => string always valid for reading *) (* Evaluates a [valid_string] or [valid_wstring] predicate. First, we check the constness of the arguments. diff --git a/src/plugins/value/utils/value_util.ml b/src/plugins/value/utils/value_util.ml index b6826afb7ea3f80eabdabd2850acb5f8de10a757..00f2871518eca24188ce2f575e0bfdc71771c7c6 100644 --- a/src/plugins/value/utils/value_util.ml +++ b/src/plugins/value/utils/value_util.ml @@ -88,7 +88,7 @@ let get_subdivision stmt = x let pretty_actuals fmt actuals = - let pp fmt (e,x,_) = Cvalue.V.pretty_typ (Some (Cil.typeOf e)) fmt x in + let pp fmt (e,x) = Cvalue.V.pretty_typ (Some (Cil.typeOf e)) fmt x in Pretty_utils.pp_flowlist pp fmt actuals let pretty_current_cfunction_name fmt = diff --git a/src/plugins/value/utils/value_util.mli b/src/plugins/value/utils/value_util.mli index 3ae37341f50d8a81d2ad457387c3cb93f789a6f3..61df6ce5331ea7d09ef8efb35e2e607bb5f3d0e5 100644 --- a/src/plugins/value/utils/value_util.mli +++ b/src/plugins/value/utils/value_util.mli @@ -48,7 +48,7 @@ val emitter : Emitter.t val get_slevel : Kernel_function.t -> Value_parameters.SlevelFunction.value val get_subdivision: stmt -> int val pretty_actuals : - Format.formatter -> (Cil_types.exp * Cvalue.V.t * 'b) list -> unit + Format.formatter -> (Cil_types.exp * Cvalue.V.t) list -> unit val pretty_current_cfunction_name : Format.formatter -> unit val warning_once_current : ('a, Format.formatter, unit) format -> 'a diff --git a/src/plugins/value/value_parameters.ml b/src/plugins/value/value_parameters.ml index 7714801f87bdc613d7a694f723c721172a12bfbf..ec4e77fd3ed01fec6b323a0129a6a1f580db8bd0 100644 --- a/src/plugins/value/value_parameters.ml +++ b/src/plugins/value/value_parameters.ml @@ -885,6 +885,10 @@ let () = add_precision_dep ILevel.parameter let () = ILevel.add_update_hook (fun _ i -> Int_set.set_small_cardinal i) let () = ILevel.set_range 4 256 +let builtins = ref Datatype.String.Set.empty +let register_builtin name = builtins := Datatype.String.Set.add name !builtins +let mem_builtin name = Datatype.String.Set.mem name !builtins + let () = Parameter_customize.set_group precision_tuning let () = Parameter_customize.argument_may_be_fundecl () module BuiltinsOverrides = @@ -895,12 +899,12 @@ module BuiltinsOverrides = let of_string ~key:kf ~prev:_ nameopt = begin match nameopt with | Some name -> - if not (!Db.Value.mem_builtin name) then + if not (mem_builtin name) then abort "option '-eva-builtin %a:%s': undeclared builtin '%s'@.\ declared builtins: @[%a@]" Kernel_function.pretty kf name name (Pretty_utils.pp_list ~sep:",@ " Format.pp_print_string) - (List.map fst (!Db.Value.registered_builtins ())) + (Datatype.String.Set.elements !builtins) | _ -> abort "option '-eva-builtin':@ \ no builtin associated to function '%a',@ use '%a:<builtin>'" @@ -921,7 +925,7 @@ let () = add_correctness_dep BuiltinsOverrides.parameter (* Exported in Eva.mli. *) let use_builtin key name = - if !Db.Value.mem_builtin name + if mem_builtin name then BuiltinsOverrides.add (key, Some name) else raise Not_found diff --git a/src/plugins/value/value_parameters.mli b/src/plugins/value/value_parameters.mli index 0a294b1e8c116567bfb723b1843c173484228d91..3f50d6efaa7990c3c68289b0043e4259bab34ba0 100644 --- a/src/plugins/value/value_parameters.mli +++ b/src/plugins/value/value_parameters.mli @@ -218,6 +218,9 @@ val dkey_callbacks : category (** Debug category used to print the usage of widenings. *) val dkey_widening : category +(** Registers available cvalue builtins for the -eva-builtin option. *) +val register_builtin: string -> unit + (** Registers available domain names for the -eva-domains option. *) val register_domain: name:string -> descr:string -> unit diff --git a/src/plugins/value_types/value_types.ml b/src/plugins/value_types/value_types.ml index 724f9d5b835abcaad649f350df881c8371fc8f52..b115bc2ce41b7ea3f80a549a13432a606852a64e 100644 --- a/src/plugins/value_types/value_types.ml +++ b/src/plugins/value_types/value_types.ml @@ -114,18 +114,7 @@ type 'a callback_result = | NormalStore of 'a * int | Reuse of int -type cacheable = - | Cacheable - | NoCache - | NoCacheCallers - - -type call_result = { - c_values: (Cvalue.V_Offsetmap.t option * Cvalue.Model.t) list; - c_clobbered: Base.SetLattice.t; - c_cacheable: cacheable; - c_from: (Function_Froms.froms * Locations.Zone.t) option -} +type call_froms = (Function_Froms.froms * Locations.Zone.t) option type logic_dependencies = Locations.Zone.t Cil_datatype.Logic_label.Map.t diff --git a/src/plugins/value_types/value_types.mli b/src/plugins/value_types/value_types.mli index f3226b1161b4a4a3d6dd83ec8e1de742b24188c8..6d5ebf1fa8704d196ec5cf2ab5f9156c590b01c2 100644 --- a/src/plugins/value_types/value_types.mli +++ b/src/plugins/value_types/value_types.mli @@ -63,37 +63,10 @@ type 'a callback_result = | NormalStore of 'a * int | Reuse of int -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 *) - - -(** Results of a a call to a function *) -type call_result = { - c_values: (** Memory states after the call *) - (Cvalue.V_Offsetmap.t option - (** the value returned (ie. what is after the 'return' C keyword). *) - * Cvalue.Model.t - (** the memory state after the function has been executed *)) - list; - - c_clobbered: Base.SetLattice.t - (** An over-approximation of the bases in which addresses of local - variables might have been written *); - - c_cacheable: cacheable - (** Is it possible to cache the result of this call? *); - - 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 the dependencies - of each zone written to. *) -} - +type call_froms = (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 the dependencies + of each zone written to. *) (** Dependencies for the evaluation of a term or a predicate: for each program point involved, sets of zones that must be read *) diff --git a/tests/builtins/oracle/memchr.res.oracle b/tests/builtins/oracle/memchr.res.oracle index 4fbe20f941e39805a6270ea36816a8f5df791524..df4a1d526227438f0e5e72bc778aa514d5533a41 100644 --- a/tests/builtins/oracle/memchr.res.oracle +++ b/tests/builtins/oracle/memchr.res.oracle @@ -538,12 +538,12 @@ [eva] tests/builtins/memchr.c:376: assertion got status valid. [eva] tests/builtins/memchr.c:377: assertion got status valid. [eva] tests/builtins/memchr.c:379: Call to builtin memchr -[eva:alarm] tests/builtins/memchr.c:379: Warning: - function memchr: precondition 'valid' got status unknown. -[eva:alarm] tests/builtins/memchr.c:379: Warning: - function memchr: precondition 'initialization' got status unknown. -[eva:alarm] tests/builtins/memchr.c:379: Warning: - function memchr: precondition 'danglingness' got status unknown. +[eva] tests/builtins/memchr.c:379: + function memchr: precondition 'valid' got status valid. +[eva] tests/builtins/memchr.c:379: + function memchr: precondition 'initialization' got status valid. +[eva] tests/builtins/memchr.c:379: + function memchr: precondition 'danglingness' got status valid. [eva] tests/builtins/memchr.c:379: Frama_C_show_each_mymemchr: {1} [eva] tests/builtins/memchr.c:380: Call to builtin memchr [eva:alarm] tests/builtins/memchr.c:380: Warning: @@ -1308,9 +1308,15 @@ [eva] ====== VALUES COMPUTED ====== [eva:final-states] Values at end of function memchr_big_array: c ∈ {0} - u[0..199] ∈ {270729319} or UNINITIALIZED - r[0..200] ∈ {270729319} or UNINITIALIZED - t[0..999999] ∈ {270729319} or UNINITIALIZED + u[0][bits 0 to 15]# ∈ {270729319}%32, bits 0 to 15 + [bits 16 to 6399]# ∈ + {270729319} or UNINITIALIZED repeated %32, bits 16 to 6399 + r[0][bits 0 to 15]# ∈ {270729319} or UNINITIALIZED%32, bits 0 to 15 + [bits 16 to 6431]# ∈ + {270729319} or UNINITIALIZED repeated %32, bits 16 to 6431 + t[0][bits 0 to 15]# ∈ {270729319} or UNINITIALIZED%32, bits 0 to 15 + [bits 16 to 31999999]# ∈ + {270729319} or UNINITIALIZED repeated %32, bits 16 to 31999999 p ∈ {{ &t + [0..3999996],0%4 }} len_u ∈ {1} len_r ∈ {1} @@ -1339,13 +1345,12 @@ z2 ∈ {0} [eva:final-states] Values at end of function memchr_initialization: c ∈ {0} - empty_or_uninitialized[0] ∈ {0} or UNINITIALIZED + empty_or_uninitialized[0] ∈ {0} z1 ∈ {0} - s[0] ∈ {1} or UNINITIALIZED + s[0] ∈ {1} [1] ∈ {0} z2 ∈ {1} - t[0..1] ∈ {10} - [2] ∈ {10} or UNINITIALIZED + t[0..2] ∈ {10} [3] ∈ {0} z3 ∈ {3} [eva:final-states] Values at end of function memchr_misc: @@ -1381,7 +1386,7 @@ s1 ∈ {{ "mno\000pqr" ; "MNOP\000QRS" }} sz5 ∈ {3; 4} sz6 ∈ {3; 4} - maybe_init[0] ∈ {65} or UNINITIALIZED + maybe_init[0] ∈ {65} [1] ∈ {0} sz8 ∈ {1} [eva:final-states] Values at end of function memchr_misc_array: