From 84d892b5ba5645b55d5bfdc7a80b9ab006a501de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Fri, 31 Jan 2020 17:27:35 +0100 Subject: [PATCH] [Eva] Removes the [Transfer] functor from abstract domains. For abstract domains, the valuation is now a record of three functions: [find], [fold] and [find_loc]. Adds a function in the evaluation engine to convert Valuation maps into these records. --- src/plugins/value/domains/abstract_domain.mli | 41 +- .../value/domains/apron/apron_domain.ml | 227 +++++---- .../value/domains/cvalue/cvalue_domain.ml | 61 ++- .../value/domains/cvalue/cvalue_transfer.ml | 416 ++++++++--------- .../value/domains/cvalue/cvalue_transfer.mli | 20 +- src/plugins/value/domains/domain_builder.ml | 81 ++-- src/plugins/value/domains/domain_lift.ml | 71 +-- src/plugins/value/domains/domain_product.ml | 181 ++++---- .../value/domains/equality/equality_domain.ml | 435 +++++++++--------- .../value/domains/gauges/gauges_domain.ml | 193 ++++---- src/plugins/value/domains/inout_domain.ml | 63 +-- src/plugins/value/domains/octagons.ml | 246 +++++----- src/plugins/value/domains/offsm_domain.ml | 100 ++-- src/plugins/value/domains/simple_memory.ml | 178 ++++--- src/plugins/value/domains/symbolic_locs.ml | 168 ++++--- src/plugins/value/domains/traces_domain.ml | 105 ++--- src/plugins/value/domains/unit_domain.ml | 19 +- src/plugins/value/engine/evaluation.ml | 6 + src/plugins/value/engine/evaluation.mli | 5 + src/plugins/value/engine/transfer_stmt.ml | 26 +- src/plugins/value/partitioning/partition.ml | 5 +- src/plugins/value/register.ml | 4 +- 22 files changed, 1236 insertions(+), 1415 deletions(-) diff --git a/src/plugins/value/domains/abstract_domain.mli b/src/plugins/value/domains/abstract_domain.mli index 1b8ed544fad..16a7dc6c6d7 100644 --- a/src/plugins/value/domains/abstract_domain.mli +++ b/src/plugins/value/domains/abstract_domain.mli @@ -164,21 +164,26 @@ module type Queries = sig end (** Results of an evaluation: the results of all intermediate calculation (the - value of each expression and the location of each lvalue) are cached in a - map. *) -module type Valuation = sig - type t - type value (** Abstract value. *) - type origin (** Origin of abstract values. *) - type loc (** Abstract memory location. *) - val find : t -> exp -> (value, origin) record_val or_top - val fold : (exp -> (value, origin) record_val -> 'a -> 'a) -> t -> 'a -> 'a - val find_loc : t -> lval -> loc record_loc or_top -end + value of each expression and the location of each lvalue) are available for + the domain. *) +type ('value, 'location, 'origin) valuation = + { + find: exp -> ('value, 'origin) record_val or_top; + (** Finds the value computed for an expression. The returned record also + contains the origin given by the domain for this expression, the alarms + emitted by its evaluation, and whether its value has been reduced. + Returns `Top if the expression has not been evaluated. *) + fold: 'a. (exp -> ('value, 'origin) record_val -> 'a -> 'a) -> 'a -> 'a; + (** [fold f e] applies [f expr record] to any expression [expr] evaluated + to [record], which contains its value, reduction, origin, and alarms. *) + find_loc: lval -> 'location record_loc or_top + (** Finds the location computed for an lvalue. The returned record also + contains the lvalue type and the alarms emitted by its evaluation. + Returns `Top if the lvalue has not been evaluated. *) + } (** Transfer function of the domain. *) module type Transfer = sig - type state type value type location @@ -324,14 +329,10 @@ module type S = sig (** Transfer functions from the result of evaluations. See {eval.mli} for more details about valuation. *) - module Transfer - (Valuation: Valuation with type value = value - and type origin = origin - and type loc = location) - : Transfer with type state := t - and type value := value - and type location := location - and type valuation := Valuation.t + include Transfer with type state := t + and type value := value + and type location := location + and type valuation := (value, location, origin) valuation (** {3 Logic } *) diff --git a/src/plugins/value/domains/apron/apron_domain.ml b/src/plugins/value/domains/apron/apron_domain.ml index 15035124c40..22cd1448509 100644 --- a/src/plugins/value/domains/apron/apron_domain.ml +++ b/src/plugins/value/domains/apron/apron_domain.ml @@ -360,6 +360,7 @@ module Make (Man : Input) = struct type state = Man.t Abstract1.t type value = Main_values.Interval.t type location = Precise_locs.precise_location + type origin = unit let man = Man.manager let log_category = dkey @@ -427,8 +428,6 @@ module Make (Man : Input) = struct let s = Abstract1.meet man s1 s2 in if Abstract1.is_bottom man s then `Bottom else `Value s - type origin = unit - let make_eval state = let env = Abstract1.env state in fun e -> @@ -555,130 +554,124 @@ module Make (Man : Input) = struct let incr_loop_counter _ state = state let leave_loop _ state = state - module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type loc = location) - = struct - - (* make an oracle for the translation Cil->Apron, using the valuation. - Translate integer expressions that have been evaluated (which should - be all of them if the translation is called on a source expression!) - into Apron intervals. *) - let make_oracle valuation = - fun exp exn -> - if Cil.isIntegralType (Cil.typeOf exp) then - match Valuation.find valuation exp with - | `Value { value = { v = `Value itv } } -> - let interval = ival_to_interval itv in - Texpr1.Cst (Coeff.Interval interval) - | _ -> raise exn - else raise exn - - let update valuation state = - let eval = make_eval state in - let oracle = make_oracle valuation in - let env = Abstract1.env state in - (* Makes a list of apron constraints from a valuation: - for each value marked as Reduced for an expression, creates the - apron constraint [expression = value]. *) - let gather_constraints exp record acc = - if record.reductness = Reduced - then - try - let expr = translate_expr_linearize eval oracle exp in - let expr = coerce eval (Cil.typeOf exp) expr in - (* When the value is top or bottom, no constraint is expressible. *) - let cons = record.value.v >>- fun ival -> - let interval = ival_to_interval ival in - if Interval.is_top interval - then `Bottom - else `Value (constraint_reduction env expr interval) - in - Bottom.add_to_list cons acc - with Out_of_Scope _ -> acc - else acc + (* make an oracle for the translation Cil->Apron, using the valuation. + Translate integer expressions that have been evaluated (which should + be all of them if the translation is called on a source expression!) + into Apron intervals. *) + let make_oracle valuation = + fun exp exn -> + if Cil.isIntegralType (Cil.typeOf exp) then + match valuation.Abstract_domain.find exp with + | `Value { value = { v = `Value itv } } -> + let interval = ival_to_interval itv in + Texpr1.Cst (Coeff.Interval interval) + | _ -> raise exn + else raise exn + + let update valuation state = + let eval = make_eval state in + let oracle = make_oracle valuation in + let env = Abstract1.env state in + (* Makes a list of apron constraints from a valuation: + for each value marked as Reduced for an expression, creates the + apron constraint [expression = value]. *) + let gather_constraints exp record acc = + if record.reductness = Reduced + then + try + let expr = translate_expr_linearize eval oracle exp in + let expr = coerce eval (Cil.typeOf exp) expr in + (* When the value is top or bottom, no constraint is expressible. *) + let cons = record.value.v >>- fun ival -> + let interval = ival_to_interval ival in + if Interval.is_top interval + then `Bottom + else `Value (constraint_reduction env expr interval) + in + Bottom.add_to_list cons acc + with Out_of_Scope _ -> acc + else acc + in + let constraints = valuation.Abstract_domain.fold gather_constraints [] in + if constraints = [] + then `Value state + else meet_with_constraints env state constraints + + let assign _stmt lvalue expr _value valuation state = + update valuation state >>- fun state -> + try + let state = + try + let eval = make_eval state in + let oracle = make_oracle valuation in + let var = translate_lval lvalue.lval in + let expr = expr in + let exp = translate_expr_linearize eval oracle expr in + let exp = coerce eval lvalue.ltyp exp in + let exp = Texpr1.of_expr (Abstract1.env state) exp in + (* TODO: currently, all variables are present in the environment + at all times. Change to a dynamic environment, in which new + variables are added here, and removed when the scope changes. *) + Abstract1.assign_texpr man state var exp None + with + | Out_of_Scope _ -> kill_bases lvalue.lloc state in - let constraints = Valuation.fold gather_constraints valuation [] in - if constraints = [] - then `Value state - else meet_with_constraints env state constraints + maybe_bottom state + with Manager.Error exclog -> abort exclog - let assign _stmt lvalue expr _value valuation state = - update valuation state >>- fun state -> - try - let state = - try - let eval = make_eval state in - let oracle = make_oracle valuation in - let var = translate_lval lvalue.lval in - let expr = expr in - let exp = translate_expr_linearize eval oracle expr in - let exp = coerce eval lvalue.ltyp exp in - let exp = Texpr1.of_expr (Abstract1.env state) exp in - (* TODO: currently, all variables are present in the environment - at all times. Change to a dynamic environment, in which new - variables are added here, and removed when the scope changes. *) - Abstract1.assign_texpr man state var exp None - with - | Out_of_Scope _ -> kill_bases lvalue.lloc state - in - maybe_bottom state - with Manager.Error exclog -> abort exclog + let assume _stmt exp bool valuation state = + update valuation state >>- fun state -> + try + let env = Abstract1.env state in + let eval = make_eval state in + let oracle = make_oracle valuation in + let cons = constraint_expr eval oracle env exp bool in + let array = Tcons1.array_make env 1 in + Tcons1.array_set array 0 cons; + let state = Abstract1.meet_tcons_array man state array in + maybe_bottom state + with + | Out_of_Scope _ -> `Value state - let assume _stmt exp bool valuation state = - update valuation state >>- fun state -> + let start_call _stmt call valuation state = + update valuation state >>- fun state -> + let eval = make_eval state in + let oracle = make_oracle valuation in + let process_argument (vars, acc) arg = try let env = Abstract1.env state in - let eval = make_eval state in - let oracle = make_oracle valuation in - let cons = constraint_expr eval oracle env exp bool in - let array = Tcons1.array_make env 1 in - Tcons1.array_set array 0 cons; - let state = Abstract1.meet_tcons_array man state array in - maybe_bottom state + let var = translate_varinfo arg.formal in + let vars = var :: vars in + let acc = + try + let exp = translate_expr_linearize eval oracle arg.concrete in + let texp = Texpr1.of_expr env exp in + (var, texp) :: acc + with Out_of_Scope _ -> acc + in + vars, acc with - | Out_of_Scope _ -> `Value state - - let start_call _stmt call valuation state = - update valuation state >>- fun state -> - let eval = make_eval state in - let oracle = make_oracle valuation in - let process_argument (vars, acc) arg = - try - let env = Abstract1.env state in - let var = translate_varinfo arg.formal in - let vars = var :: vars in - let acc = - try - let exp = translate_expr_linearize eval oracle arg.concrete in - let texp = Texpr1.of_expr env exp in - (var, texp) :: acc - with Out_of_Scope _ -> acc - in - vars, acc - with - | Out_of_Scope _ -> (vars, acc) - in - let vars, list = List.fold_left process_argument ([], []) call.arguments in - let env = Abstract1.env state in - let vars_array = Array.of_list vars in - let env = Environment.add env vars_array [||] in - let vars, texprs = List.split list in - let vars_array = Array.of_list vars - and texprs_array = Array.of_list texprs in - let state = Abstract1.change_environment man state env false in - let state = - Abstract1.assign_texpr_array man state vars_array texprs_array None - in - if Abstract1.is_bottom man state - then `Bottom - else `Value state + | Out_of_Scope _ -> (vars, acc) + in + let vars, list = List.fold_left process_argument ([], []) call.arguments in + let env = Abstract1.env state in + let vars_array = Array.of_list vars in + let env = Environment.add env vars_array [||] in + let vars, texprs = List.split list in + let vars_array = Array.of_list vars + and texprs_array = Array.of_list texprs in + let state = Abstract1.change_environment man state env false in + let state = + Abstract1.assign_texpr_array man state vars_array texprs_array None + in + if Abstract1.is_bottom man state + then `Bottom + else `Value state - let finalize_call _stmt _call ~pre:_ ~post = `Value post + let finalize_call _stmt _call ~pre:_ ~post = `Value post - let show_expr _valuation _state _fmt _expr = () - end + let show_expr _valuation _state _fmt _expr = () let logic_assign _assigns location ~pre:_ state = kill_bases location state let evaluate_predicate _ _ _ = Alarmset.Unknown diff --git a/src/plugins/value/domains/cvalue/cvalue_domain.ml b/src/plugins/value/domains/cvalue/cvalue_domain.ml index cf96cb4f363..13142d74dcb 100644 --- a/src/plugins/value/domains/cvalue/cvalue_domain.ml +++ b/src/plugins/value/domains/cvalue/cvalue_domain.ml @@ -208,45 +208,40 @@ module State = struct Model.backward_location state lval typ precise_loc value let reduce_further _ _ _ = [] + (* ------------------------------------------------------------------------ *) + (* Transfer Functions *) + (* ------------------------------------------------------------------------ *) - module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = location) - = struct - - module T = Cvalue_transfer.Transfer (Valuation) - - let update valuation (s, clob) = T.update valuation s >>-: fun s -> s, clob + let update valuation (s, clob) = + Cvalue_transfer.update valuation s >>-: fun s -> s, clob - let assign stmt lv expr assigned valuation (s, clob) = - T.assign stmt lv expr assigned valuation s >>-: fun s -> - (* TODO: use the value in assignment *) - let _ = - Eval.value_assigned assigned >>-: fun value -> - let location = Precise_locs.imprecise_location lv.lloc in - Locals_scoping.remember_if_locals_in_value clob location value - in - s, clob + let assign stmt lv expr assigned valuation (s, clob) = + Cvalue_transfer.assign stmt lv expr assigned valuation s >>-: fun s -> + (* TODO: use the value in assignment *) + let _ = + Eval.value_assigned assigned >>-: fun value -> + let location = Precise_locs.imprecise_location lv.lloc in + Locals_scoping.remember_if_locals_in_value clob location value + in + s, clob - let assume stmt expr positive valuation (s, clob) = - T.assume stmt expr positive valuation s >>-: fun s -> - s, clob + let assume stmt expr positive valuation (s, clob) = + Cvalue_transfer.assume stmt expr positive valuation s >>-: fun s -> + s, clob - let start_call stmt call valuation (s, _clob) = - T.start_call stmt call valuation s >>-: fun state -> - state, Locals_scoping.bottom () + let start_call stmt call valuation (s, _clob) = + Cvalue_transfer.start_call stmt call valuation s >>-: fun state -> + state, Locals_scoping.bottom () - let finalize_call stmt call ~pre ~post = - let (post_state, post_clob) = post - and pre_state, clob = pre in - Locals_scoping.(remember_bases_with_locals clob post_clob.clob); - T.finalize_call stmt call ~pre:pre_state ~post:post_state - >>-: fun state -> - state, clob + let finalize_call stmt call ~pre ~post = + let (post_state, post_clob) = post + and pre_state, clob = pre in + Locals_scoping.(remember_bases_with_locals clob post_clob.clob); + Cvalue_transfer.finalize_call stmt call ~pre:pre_state ~post:post_state + >>-: fun state -> + state, clob - let show_expr valuation (state, _) = T.show_expr valuation state - end + let show_expr valuation (state, _) = Cvalue_transfer.show_expr valuation state (* ------------------------------------------------------------------------ *) (* Mem Exec *) diff --git a/src/plugins/value/domains/cvalue/cvalue_transfer.ml b/src/plugins/value/domains/cvalue/cvalue_transfer.ml index 1e9065ac7f3..a0f76b6a4a5 100644 --- a/src/plugins/value/domains/cvalue/cvalue_transfer.ml +++ b/src/plugins/value/domains/cvalue/cvalue_transfer.ml @@ -22,230 +22,220 @@ open Cil_types open Eval - open Cvalue.Model type value = Main_values.CVal.t +type origin = value option type location = Main_locations.PLoc.location - let unbottomize = function | `Bottom -> Cvalue.V.bottom | `Value v -> v -module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type origin = value option - and type loc = location) -= struct - - type state = Cvalue.Model.t - - (* ---------------------------------------------------------------------- *) - (* Assumptions *) - (* ---------------------------------------------------------------------- *) - - let reduce valuation lval value t = - let typ = Cil.typeOfLval lval in +(* ---------------------------------------------------------------------- *) +(* Assumptions *) +(* ---------------------------------------------------------------------- *) + +let reduce valuation lval value t = + let typ = Cil.typeOfLval lval in + if Cil.typeHasQualifier "volatile" typ + then t + else + match valuation.Abstract_domain.find_loc lval with + | `Value record -> + let loc = Precise_locs.imprecise_location record.loc in + if Locations.cardinal_zero_or_one loc + then reduce_indeterminate_binding t loc value + else t + | `Top -> t (* Cannot reduce without the location of the lvalue. *) + +let is_smaller_value typ v1 v2 = + let size = Integer.of_int (Cil.bitsSizeOf typ) in + let card1 = Cvalue.V.cardinal_estimate v1 ~size + and card2 = Cvalue.V.cardinal_estimate v2 ~size in + Integer.lt card1 card2 + +(* Update the state according to a Valuation. *) +let update valuation t = + let process exp record t = + match exp.enode with + | Lval lv -> + if record.reductness = Reduced + then + let {v; initialized; escaping} = record.value in + let v = unbottomize v in + let v = + (* The origin contains the value already stored in the state, when + its type is incompatible with the lvalue [lv]. The precision of + this previous value and [v] are then incomparable (none is + included in the other). We use some notion of cardinality of + abstract values to choose the best value to keep. *) + match record.origin with + | Some (Some previous_v) -> + let typ = Cil.typeOfLval lv in + if is_smaller_value typ v previous_v then v else previous_v + | _ -> v + in + let value = Cvalue.V_Or_Uninitialized.make ~initialized ~escaping v in + reduce valuation lv value t + else t + | _ -> t + in + valuation.Abstract_domain.fold process t + +(* ---------------------------------------------------------------------- *) +(* Assignments *) +(* ---------------------------------------------------------------------- *) + +let write_abstract_value state (lval, loc, typ) assigned_value = + let {v; initialized; escaping} = assigned_value in + let value = unbottomize v in + Warn.warn_right_exp_imprecision lval loc value; + let value = if Cil.typeHasQualifier "volatile" typ - then t - else - match Valuation.find_loc valuation lval with - | `Value record -> - let loc = Precise_locs.imprecise_location record.loc in - if Locations.cardinal_zero_or_one loc - then reduce_indeterminate_binding t loc value - else t - | `Top -> t (* Cannot reduce without the location of the lvalue. *) - - let is_smaller_value typ v1 v2 = - let size = Integer.of_int (Cil.bitsSizeOf typ) in - let card1 = Cvalue.V.cardinal_estimate v1 ~size - and card2 = Cvalue.V.cardinal_estimate v2 ~size in - Integer.lt card1 card2 - - (* Update the state according to a Valuation. *) - let update valuation t = - let process exp record t = - match exp.enode with - | Lval lv -> - if record.reductness = Reduced - then - let {v; initialized; escaping} = record.value in - let v = unbottomize v in - let v = - (* The origin contains the value already stored in the state, when - its type is incompatible with the lvalue [lv]. The precision of - this previous value and [v] are then incomparable (none is - included in the other). We use some notion of cardinality of - abstract values to choose the best value to keep. *) - match record.origin with - | Some (Some previous_v) -> - let typ = Cil.typeOfLval lv in - if is_smaller_value typ v previous_v then v else previous_v - | _ -> v - in - let value = Cvalue.V_Or_Uninitialized.make ~initialized ~escaping v in - reduce valuation lv value t - else t - | _ -> t - in - Valuation.fold process valuation t - - (* ---------------------------------------------------------------------- *) - (* Assignments *) - (* ---------------------------------------------------------------------- *) - - let write_abstract_value state (lval, loc, typ) assigned_value = - let {v; initialized; escaping} = assigned_value in - let value = unbottomize v in - Warn.warn_right_exp_imprecision lval loc value; + then Cvalue_forward.make_volatile value + else value + in + match loc.Locations.loc with + | Locations.Location_Bits.Top (Base.SetLattice.Top, orig) -> + Value_parameters.result + "State before degeneration:@\n======%a@\n=======" + Cvalue.Model.pretty state; + Value_util.warning_once_current + "writing at a completely unknown address@[%a@].@\nAborting." + Origin.pretty_as_reason orig; + raise Db.Value.Aborted + | _ -> + let exact = Locations.cardinal_zero_or_one loc in let value = - if Cil.typeHasQualifier "volatile" typ - then Cvalue_forward.make_volatile value - else value - in - match loc.Locations.loc with - | Locations.Location_Bits.Top (Base.SetLattice.Top, orig) -> - Value_parameters.result - "State before degeneration:@\n======%a@\n=======" - Cvalue.Model.pretty state; - Value_util.warning_once_current - "writing at a completely unknown address@[%a@].@\nAborting." - Origin.pretty_as_reason orig; - raise Db.Value.Aborted - | _ -> - let exact = Locations.cardinal_zero_or_one loc in - let value = - Cvalue.V_Or_Uninitialized.make ~initialized ~escaping value in - (* let value = Cvalue.V_Or_Uninitialized.initialized value in *) - add_indeterminate_binding ~exact state loc value - - exception Do_assign_imprecise_copy - - let copy_one_loc state left_lv right_lv = - let left_lval, left_loc, left_typ = left_lv - and right_lval, right_loc, right_typ = right_lv in - (* Warn if right_loc is imprecise *) - Warn.warn_imprecise_lval_read right_lval right_loc Cvalue.V.bottom; - (* top size is tested before this function is called, in which case - the imprecise copy mode is used. *) - let size = Int_Base.project right_loc.Locations.size in - let offsetmap = copy_offsetmap right_loc.Locations.loc size state in - let make_volatile = - Cil.typeHasQualifier "volatile" left_typ || - Cil.typeHasQualifier "volatile" right_typ - in - match offsetmap with - | `Bottom -> `Bottom - | `Value offsm -> - (* TODO: this is the good place to handle partially volatile - struct, whether as source or destination *) - let offsetmap = - if make_volatile then - Cvalue.V_Offsetmap.map_on_values - (Cvalue.V_Or_Uninitialized.map Cvalue_forward.make_volatile) offsm - else offsm - in - if not (Eval_typ.offsetmap_matches_type left_typ offsetmap) then - raise Do_assign_imprecise_copy; - Cvalue_offsetmap.warn_right_imprecision left_lval left_loc offsetmap; - `Value - (paste_offsetmap ~exact:true - ~from:offsetmap ~dst_loc:left_loc.Locations.loc ~size state) - - let make_determinate value = - { v = `Value value; initialized = true; escaping = false } - - let copy_right_lval state left_lv right_lv copied_value = - let lval, loc, typ = left_lv in - (* Size mismatch between left and right size, or imprecise size. - This cannot be done by copies, but require a conversion *) - let right_size = Main_locations.PLoc.size right_lv.lloc - and left_size = Main_locations.PLoc.size loc in - if not (Int_Base.equal left_size right_size) || Int_Base.is_top right_size - then - fun loc -> write_abstract_value state (lval, loc, typ) copied_value - else - fun loc -> - try - let process right_loc acc = - let left_lv = lval, loc, typ - and right_lv = right_lv.lval, right_loc, right_lv.ltyp in - match copy_one_loc state left_lv right_lv with - | `Bottom -> acc - | `Value state -> join acc state - in - Precise_locs.fold process right_lv.lloc bottom - with - Do_assign_imprecise_copy -> - write_abstract_value state (lval, loc, typ) copied_value - - let assign _stmt { lval; ltyp; lloc } _expr assigned valuation state = - let state = update valuation state in - let assign_one_loc = - match assigned with - | Assign value -> - let assigned_value = make_determinate value in - fun loc -> write_abstract_value state (lval, loc, ltyp) assigned_value - | Copy (right_lv, copied_value) -> - copy_right_lval state (lval, lloc, ltyp) right_lv copied_value - in - let aux_loc loc acc_state = - let s = assign_one_loc loc in - join acc_state s + Cvalue.V_Or_Uninitialized.make ~initialized ~escaping value in + (* let value = Cvalue.V_Or_Uninitialized.initialized value in *) + add_indeterminate_binding ~exact state loc value + +exception Do_assign_imprecise_copy + +let copy_one_loc state left_lv right_lv = + let left_lval, left_loc, left_typ = left_lv + and right_lval, right_loc, right_typ = right_lv in + (* Warn if right_loc is imprecise *) + Warn.warn_imprecise_lval_read right_lval right_loc Cvalue.V.bottom; + (* top size is tested before this function is called, in which case + the imprecise copy mode is used. *) + let size = Int_Base.project right_loc.Locations.size in + let offsetmap = copy_offsetmap right_loc.Locations.loc size state in + let make_volatile = + Cil.typeHasQualifier "volatile" left_typ || + Cil.typeHasQualifier "volatile" right_typ + in + match offsetmap with + | `Bottom -> `Bottom + | `Value offsm -> + (* TODO: this is the good place to handle partially volatile + struct, whether as source or destination *) + let offsetmap = + if make_volatile then + Cvalue.V_Offsetmap.map_on_values + (Cvalue.V_Or_Uninitialized.map Cvalue_forward.make_volatile) offsm + else offsm in - let state = Precise_locs.fold aux_loc lloc bottom in - if not (is_reachable state) - then `Bottom - else `Value state - - (* ---------------------------------------------------------------------- *) - (* Function Calls *) - (* ---------------------------------------------------------------------- *) - - let actualize_formals state arguments = - let treat_one_formal state arg = - let offsm = - Cvalue_offsetmap.offsetmap_of_assignment state arg.concrete arg.avalue - in - Cvalue.Model.add_base (Base.of_varinfo arg.formal) offsm state + if not (Eval_typ.offsetmap_matches_type left_typ offsetmap) then + raise Do_assign_imprecise_copy; + Cvalue_offsetmap.warn_right_imprecision left_lval left_loc offsetmap; + `Value + (paste_offsetmap ~exact:true + ~from:offsetmap ~dst_loc:left_loc.Locations.loc ~size state) + +let make_determinate value = + { v = `Value value; initialized = true; escaping = false } + +let copy_right_lval state left_lv right_lv copied_value = + let lval, loc, typ = left_lv in + (* Size mismatch between left and right size, or imprecise size. + This cannot be done by copies, but require a conversion *) + let right_size = Main_locations.PLoc.size right_lv.lloc + and left_size = Main_locations.PLoc.size loc in + if not (Int_Base.equal left_size right_size) || Int_Base.is_top right_size + then + fun loc -> write_abstract_value state (lval, loc, typ) copied_value + else + fun loc -> + try + let process right_loc acc = + let left_lv = lval, loc, typ + and right_lv = right_lv.lval, right_loc, right_lv.ltyp in + match copy_one_loc state left_lv right_lv with + | `Bottom -> acc + | `Value state -> join acc state + in + Precise_locs.fold process right_lv.lloc bottom + with + Do_assign_imprecise_copy -> + write_abstract_value state (lval, loc, typ) copied_value + +let assign _stmt { lval; ltyp; lloc } _expr assigned valuation state = + let state = update valuation state in + let assign_one_loc = + match assigned with + | Assign value -> + let assigned_value = make_determinate value in + fun loc -> write_abstract_value state (lval, loc, ltyp) assigned_value + | Copy (right_lv, copied_value) -> + copy_right_lval state (lval, lloc, ltyp) right_lv copied_value + in + let aux_loc loc acc_state = + let s = assign_one_loc loc in + join acc_state s + in + let state = Precise_locs.fold aux_loc lloc bottom in + if not (is_reachable state) + then `Bottom + else `Value state + +(* ---------------------------------------------------------------------- *) +(* Function Calls *) +(* ---------------------------------------------------------------------- *) + +let actualize_formals state arguments = + let treat_one_formal state arg = + let offsm = + Cvalue_offsetmap.offsetmap_of_assignment state arg.concrete arg.avalue in - List.fold_left treat_one_formal state arguments - - let start_call _stmt call valuation state = - let state = update valuation state in - let with_formals = actualize_formals state call.arguments in - let stack_with_call = Value_util.call_stack () in - Db.Value.Call_Value_Callbacks.apply (with_formals, stack_with_call); - `Value with_formals - - let finalize_call stmt call ~pre:_ ~post:state = - (* Deallocate memory allocated via alloca(). - To minimize computations, only do it for function definitions. *) - let state' = - if Kernel_function.is_definition call.kf then - let stack = (call.kf, Kstmt stmt) :: (Value_util.call_stack ()) in - Builtins_malloc.free_automatic_bases stack state - else state + Cvalue.Model.add_base (Base.of_varinfo arg.formal) offsm state + in + List.fold_left treat_one_formal state arguments + +let start_call _stmt call valuation state = + let state = update valuation state in + let with_formals = actualize_formals state call.arguments in + let stack_with_call = Value_util.call_stack () in + Db.Value.Call_Value_Callbacks.apply (with_formals, stack_with_call); + `Value with_formals + +let finalize_call stmt call ~pre:_ ~post:state = + (* Deallocate memory allocated via alloca(). + To minimize computations, only do it for function definitions. *) + let state' = + if Kernel_function.is_definition call.kf then + let stack = (call.kf, Kstmt stmt) :: (Value_util.call_stack ()) in + Builtins_malloc.free_automatic_bases stack state + else state + in + `Value state' + +let show_expr valuation state fmt expr = + match expr.enode with + | Lval lval -> + let record = match valuation.Abstract_domain.find_loc lval with + | `Value record -> record + | `Top -> assert false in - `Value state' - - let show_expr valuation state fmt expr = - match expr.enode with - | Lval lval -> - let record = match Valuation.find_loc valuation lval with - | `Value record -> record - | `Top -> assert false - in - let offsm = Cvalue_offsetmap.offsetmap_of_lval state lval record.loc in - let typ = Cil.typeOf expr in - Eval_op.pretty_offsetmap typ fmt offsm - | _ -> Format.fprintf fmt "%s" (Unicode.top_string ()) - - - (* ----------------- Export assumption functions -------------------------- *) - - let update valuation state = `Value (update valuation state) - let assume _stmt _expr _positive = update -end + let offsm = Cvalue_offsetmap.offsetmap_of_lval state lval record.loc in + let typ = Cil.typeOf expr in + Eval_op.pretty_offsetmap typ fmt offsm + | _ -> Format.fprintf fmt "%s" (Unicode.top_string ()) + + +(* ----------------- Export assumption functions -------------------------- *) + +let update valuation state = `Value (update valuation state) +let assume _stmt _expr _positive = update diff --git a/src/plugins/value/domains/cvalue/cvalue_transfer.mli b/src/plugins/value/domains/cvalue/cvalue_transfer.mli index a2bff4ae04a..62ce0a5523c 100644 --- a/src/plugins/value/domains/cvalue/cvalue_transfer.mli +++ b/src/plugins/value/domains/cvalue/cvalue_transfer.mli @@ -23,22 +23,14 @@ (** Transfer functions for the main domain of the Value analysis. *) type value = Main_values.CVal.t +type origin = value option type location = Main_locations.PLoc.location -module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type origin = value option - and type loc = location) - : sig - - include Abstract_domain.Transfer - with type state = Cvalue.Model.t - and type value := value - and type location := location - and type valuation := Valuation.t - - end - +include Abstract_domain.Transfer + with type state := Cvalue.Model.t + and type value := value + and type location := location + and type valuation := (value, location, origin) Abstract_domain.valuation (* Local Variables: diff --git a/src/plugins/value/domains/domain_builder.ml b/src/plugins/value/domains/domain_builder.ml index fa69cac6e2a..ae604a6b2b0 100644 --- a/src/plugins/value/domains/domain_builder.ml +++ b/src/plugins/value/domains/domain_builder.ml @@ -75,28 +75,21 @@ module Make_Minimal let backward_location _state _lval _typ location value = `Value (location, value) let reduce_further _sttae _expr _value = [] - module Transfer - (_: Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = location) - = struct + let update _valuation state = `Value state - let update _valuation state = `Value state + let assign kinstr lv expr _value _valuation state = + Domain.assign kinstr lv.Eval.lval expr state - let assign kinstr lv expr _value _valuation state = - Domain.assign kinstr lv.Eval.lval expr state + let assume stmt expr positive _valuation state = + Domain.assume stmt expr positive state - let assume stmt expr positive _valuation state = - Domain.assume stmt expr positive state + let start_call stmt call _valuation state = + `Value (Domain.start_call stmt (simplify_call call) state) - let start_call stmt call _valuation state = - `Value (Domain.start_call stmt (simplify_call call) state) + let finalize_call stmt call ~pre ~post = + Domain.finalize_call stmt (simplify_call call) ~pre ~post - let finalize_call stmt call ~pre ~post = - Domain.finalize_call stmt (simplify_call call) ~pre ~post - - let show_expr _valuation = Domain.show_expr - end + let show_expr _valuation = Domain.show_expr let enter_loop _stmt state = state let incr_loop_counter _stmt state = state @@ -209,37 +202,29 @@ module Complete_Simple_Cvalue (Domain: Simpler_domains.Simple_Cvalue) let reduce_further _state _expr _value = [] - module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = location) - = struct - - let find valuation expr = - match Valuation.find valuation expr with - | `Top -> `Top - | `Value record -> `Value record.value - - let find_loc valuation lval = - match Valuation.find_loc valuation lval with - | `Top -> `Top - | `Value record -> `Value record.loc - - let record valuation = { find = find valuation; - find_loc = find_loc valuation; } - - let update _valuation state = `Value state - let assign kinstr lv expr value valuation state = - Domain.assign kinstr lv expr value (record valuation) state - let assume stmt expr positive valuation state = - Domain.assume stmt expr positive (record valuation) state - let start_call stmt call valuation state = - `Value (Domain.start_call stmt call (record valuation) state) - let finalize_call = Domain.finalize_call - - let show_expr _valuation = Domain.show_expr - - end + let find valuation expr = + match valuation.Abstract_domain.find expr with + | `Top -> `Top + | `Value record -> `Value record.value + + let find_loc valuation lval = + match valuation.Abstract_domain.find_loc lval with + | `Top -> `Top + | `Value record -> `Value record.loc + + let record valuation = { find = find valuation; + find_loc = find_loc valuation; } + + let update _valuation state = `Value state + let assign kinstr lv expr value valuation state = + Domain.assign kinstr lv expr value (record valuation) state + let assume stmt expr positive valuation state = + Domain.assume stmt expr positive (record valuation) state + let start_call stmt call valuation state = + `Value (Domain.start_call stmt call (record valuation) state) + let finalize_call = Domain.finalize_call + + let show_expr _valuation = Domain.show_expr let enter_loop _stmt state = state let incr_loop_counter _stmt state = state diff --git a/src/plugins/value/domains/domain_lift.ml b/src/plugins/value/domains/domain_lift.ml index 0256e97c80d..4aff7889345 100644 --- a/src/plugins/value/domains/domain_lift.ml +++ b/src/plugins/value/domains/domain_lift.ml @@ -51,7 +51,6 @@ module Make type value = Convert.extended_value type location = Convert.extended_location - type origin = Domain.origin let extract_expr oracle state exp = @@ -92,57 +91,37 @@ module Make in { call with arguments; rest } - module Transfer - (Valuation: - Abstract_domain.Valuation with type value = Convert.extended_value - and type origin = Domain.origin - and type loc = Convert.extended_location) - = struct - - module Internal_Valuation = struct - type t = Valuation.t - type value = Domain.value - type origin = Domain.origin - type loc = Domain.location - - let lift_record record = - { record with value = lift_flagged_value record.value } - - let find valuation expr = match Valuation.find valuation expr with - | `Value record -> `Value (lift_record record) - | `Top -> `Top - - let fold f valuation acc = - Valuation.fold - (fun exp record acc -> f exp (lift_record record) acc) - valuation acc - - let find_loc valuation loc = match Valuation.find_loc valuation loc with - | `Value r -> `Value {r with loc = Convert.restrict_loc r.loc} - | `Top -> `Top - - end - - module Internal_Transfer = Domain.Transfer (Internal_Valuation) + let lift_valuation valuation = + let (>>>) v f = match v with + | `Value v -> `Value (f v) + | `Top -> `Top + in + let lift_record r = { r with value = lift_flagged_value r.value } in + let lift_loc_record r = { r with loc = Convert.restrict_loc r.loc } in + let open Abstract_domain in + let find expr = valuation.find expr >>> lift_record in + let find_loc lval = valuation.find_loc lval >>> lift_loc_record in + let fold f acc = + valuation.fold (fun exp record acc -> f exp (lift_record record) acc) acc + in + { find; fold; find_loc; } - let update = Internal_Transfer.update + let update valuation = Domain.update (lift_valuation valuation) - let assign stmt lv expr value valuation state = - Internal_Transfer.assign stmt - (lift_left lv) expr (lift_assigned value) valuation state + let assign stmt lv expr value valuation state = + Domain.assign stmt + (lift_left lv) expr (lift_assigned value) (lift_valuation valuation) state - let assume = Internal_Transfer.assume + let assume stmt expr positive valuation state = + Domain.assume stmt expr positive (lift_valuation valuation) state - let start_call stmt call valuation state = - let call = lift_call call in - Internal_Transfer.start_call stmt call valuation state + let start_call stmt call valuation state = + Domain.start_call stmt (lift_call call) (lift_valuation valuation) state - let finalize_call stmt call ~pre ~post = - let call = lift_call call in - Internal_Transfer.finalize_call stmt call ~pre ~post + let finalize_call stmt call ~pre ~post = + Domain.finalize_call stmt (lift_call call) ~pre ~post - let show_expr = Internal_Transfer.show_expr - end + let show_expr valuation = Domain.show_expr (lift_valuation valuation) let logic_assign assigns location ~pre state = Domain.logic_assign assigns (Convert.restrict_loc location) ~pre state diff --git a/src/plugins/value/domains/domain_product.ml b/src/plugins/value/domains/domain_product.ml index 1985b6c173a..5f9d14b4397 100644 --- a/src/plugins/value/domains/domain_product.ml +++ b/src/plugins/value/domains/domain_product.ml @@ -111,115 +111,94 @@ module Make (Left.reduce_further left expr value) (Right.reduce_further right expr value) - module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = location) - = struct - - module type Lift = sig - type o - val side : origin -> reductness * o - end - - module Lift_Valuation (Lift: Lift) = struct - type t = Valuation.t - type value = Value.t - type origin = Lift.o - type loc = Valuation.loc - - let lift_record record = - let origin = Extlib.opt_map Lift.side record.origin in - let reductness = - match record.reductness, origin with - | Unreduced, Some (reduced, _) -> reduced - | Unreduced, None -> Unreduced (* This case should not happen. *) - | Reduced, Some (Created, _) -> Created - | _ as x, _ -> x - in - let origin = Extlib.opt_map snd origin in - { record with origin; reductness } - - let find valuation expr = match Valuation.find valuation expr with - | `Value record -> `Value (lift_record record) - | `Top -> `Top - - let fold f valuation acc = - Valuation.fold - (fun exp record acc -> f exp (lift_record record) acc) - valuation acc - - let find_loc = Valuation.find_loc - end + let lift_record side record = + let origin = Extlib.opt_map side record.origin in + let reductness = + match record.reductness, origin with + | Unreduced, Some (reduced, _) -> reduced + | Unreduced, None -> Unreduced (* This case should not happen. *) + | Reduced, Some (Created, _) -> Created + | _ as x, _ -> x + in + let origin = Extlib.opt_map snd origin in + { record with origin; reductness } + + let lift_valuation side valuation = + let find expr = + match valuation.Abstract_domain.find expr with + | `Value record -> `Value (lift_record side record) + | `Top -> `Top + in + let fold f acc = + valuation.Abstract_domain.fold + (fun expr record acc -> f expr (lift_record side record) acc) + acc + in + Abstract_domain.{ find; fold; find_loc = valuation.find_loc } - module Left_Valuation = - Lift_Valuation (struct type o = Left.origin let side o = o.left end) - module Right_Valuation = - Lift_Valuation (struct type o = Right.origin let side o = o.right end) + let left_val = lift_valuation (fun o -> o.left) + let right_val = lift_valuation (fun o -> o.right) - module Left_Transfer = Left.Transfer (Left_Valuation) - module Right_Transfer = Right.Transfer (Right_Valuation) - let update valuation (left, right) = - Left_Transfer.update valuation left >>- fun left -> - Right_Transfer.update valuation right >>-: fun right -> - left, right + let update valuation (left, right) = + Left.update (left_val valuation) left >>- fun left -> + Right.update (right_val valuation) right >>-: fun right -> + left, right - let assign stmt lv expr value valuation (left, right) = - Left_Transfer.assign stmt lv expr value valuation left >>- fun left -> - Right_Transfer.assign stmt lv expr value valuation right >>-: fun right -> - left, right + let assign stmt lv expr value valuation (left, right) = + Left.assign stmt lv expr value (left_val valuation) left >>- fun left -> + Right.assign stmt lv expr value (right_val valuation) right >>-: fun right -> + left, right - let assume stmt expr positive valuation (left, right) = - Left_Transfer.assume stmt expr positive valuation left >>- fun left -> - Right_Transfer.assume stmt expr positive valuation right >>-: fun right -> - left, right + let assume stmt expr positive valuation (left, right) = + Left.assume stmt expr positive (left_val valuation) left >>- fun left -> + Right.assume stmt expr positive (right_val valuation) right >>-: fun right -> + left, right - let finalize_call stmt call ~pre ~post = - let pre_left, pre_right = pre - and left_state, right_state = post in - Left_Transfer.finalize_call stmt call ~pre:pre_left ~post:left_state - >>- fun left -> - Right_Transfer.finalize_call stmt call ~pre:pre_right ~post:right_state - >>-: fun right -> - left, right + let finalize_call stmt call ~pre ~post = + let pre_left, pre_right = pre + and left_state, right_state = post in + Left.finalize_call stmt call ~pre:pre_left ~post:left_state + >>- fun left -> + Right.finalize_call stmt call ~pre:pre_right ~post:right_state + >>-: fun right -> + left, right - let start_call stmt call valuation (left, right) = - Left_Transfer.start_call stmt call valuation left >>- fun left -> - Right_Transfer.start_call stmt call valuation right >>-: fun right -> - left, right + let start_call stmt call valuation (left, right) = + Left.start_call stmt call (left_val valuation) left >>- fun left -> + Right.start_call stmt call (right_val valuation) right >>-: fun right -> + left, right - let show_expr = - let (|-) f g = fun fmt exp -> f fmt exp; g fmt exp in - let show_expr_one_side category name show_expr = fun fmt exp -> - if Value_parameters.is_debug_key_enabled category - then Format.fprintf fmt "@,@]@[<v># %s: @[<hov>%a@]" name show_expr exp - in - let right_log = Right.log_category - and left_log = Left.log_category in - match left_log = product_category, - right_log = product_category with - | true, true -> - (fun valuation (left, right) -> - Left_Transfer.show_expr valuation left |- - Right_Transfer.show_expr valuation right) - | true, false -> - (fun valuation (left, right) -> - Left_Transfer.show_expr valuation left |- - show_expr_one_side right_log Right.name - (Right_Transfer.show_expr valuation right)) - | false, true -> - (fun valuation (left, right) -> - show_expr_one_side left_log Left.name - (Left_Transfer.show_expr valuation left) |- - Right_Transfer.show_expr valuation right) - | false, false -> - (fun valuation (left, right) -> - show_expr_one_side left_log Left.name - (Left_Transfer.show_expr valuation left) |- - show_expr_one_side right_log Right.name - (Right_Transfer.show_expr valuation right)) - end + let show_expr = + let (|-) f g = fun fmt exp -> f fmt exp; g fmt exp in + let show_expr_one_side category name show_expr = fun fmt exp -> + if Value_parameters.is_debug_key_enabled category + then Format.fprintf fmt "@,@]@[<v># %s: @[<hov>%a@]" name show_expr exp + in + let right_log = Right.log_category + and left_log = Left.log_category in + match left_log = product_category, + right_log = product_category with + | true, true -> + (fun valuation (left, right) -> + Left.show_expr (left_val valuation) left |- + Right.show_expr (right_val valuation) right) + | true, false -> + (fun valuation (left, right) -> + Left.show_expr (left_val valuation) left |- + show_expr_one_side right_log Right.name + (Right.show_expr (right_val valuation) right)) + | false, true -> + (fun valuation (left, right) -> + show_expr_one_side left_log Left.name + (Left.show_expr (left_val valuation) left) |- + Right.show_expr (right_val valuation) right) + | false, false -> + (fun valuation (left, right) -> + show_expr_one_side left_log Left.name + (Left.show_expr (left_val valuation) left) |- + show_expr_one_side right_log Right.name + (Right.show_expr (right_val valuation) right)) let pretty = let print_one_side fmt category name dump state = diff --git a/src/plugins/value/domains/equality/equality_domain.ml b/src/plugins/value/domains/equality/equality_domain.ml index 7186aaebe57..9407df3145a 100644 --- a/src/plugins/value/domains/equality/equality_domain.ml +++ b/src/plugins/value/domains/equality/equality_domain.ml @@ -281,234 +281,227 @@ module Make let zone = List.fold_left aux_vi Locations.Zone.bottom vars in kill Hcexprs.Deleted zone state - module Transfer - (Valuation: Abstract_domain.Valuation - with type value = Value.t - and type loc = Precise_locs.precise_location) - = struct - - let find_val valuation expr = - match Valuation.find valuation expr with - | `Top -> Value.top - | `Value record -> Bottom.non_bottom record.value.v - - let minus_zero = Cvalue.V.inject_float Fval.minus_zero - let plus_zero = Cvalue.V.inject_float Fval.plus_zero - - let incompatible_zeros v1 v2 = - let aux v1 v2 = - Cvalue.V.(is_included minus_zero v1 && is_included plus_zero v2) + let find_val valuation expr = + match valuation.Abstract_domain.find expr with + | `Top -> Value.top + | `Value record -> Bottom.non_bottom record.value.v + + let minus_zero = Cvalue.V.inject_float Fval.minus_zero + let plus_zero = Cvalue.V.inject_float Fval.plus_zero + + let incompatible_zeros v1 v2 = + let aux v1 v2 = + Cvalue.V.(is_included minus_zero v1 && is_included plus_zero v2) + in + aux v1 v2 || aux v2 v1 + + (* Does the equality between two expressions imply they have the same + object representation, allowing the narrow of their abstract values? *) + let is_safe_equality = + match get_cvalue with + | None -> fun _ _ _ -> false + | Some get_cvalue -> + fun valuation e1 e2 -> + let cval1 = get_cvalue (find_val valuation e1) + and cval2 = get_cvalue (find_val valuation e2) in + Cvalue_forward.are_comparable Abstract_interp.Comp.Eq cval1 cval2 + && not (incompatible_zeros cval1 cval2) + + exception Top_location + + let find_loc valuation = fun lval -> + match valuation.Abstract_domain.find_loc lval with + | `Top -> raise Top_location + | `Value record -> record.loc + + let add_one_dep valuation lval deps = + match HCE.get lval with + | E _ -> assert false + | LV lv -> + let zone = + match lv with + | Var vi, NoOffset -> Locations.zone_of_varinfo vi + | _ -> + let expr = Cil.dummy_exp (Lval lv) in + Value_util.zone_of_expr (find_loc valuation) expr in - aux v1 v2 || aux v2 v1 - - (* Does the equality between two expressions imply they have the same - object representation, allowing the narrow of their abstract values? *) - let is_safe_equality = - match get_cvalue with - | None -> fun _ _ _ -> false - | Some get_cvalue -> - fun valuation e1 e2 -> - let cval1 = get_cvalue (find_val valuation e1) - and cval2 = get_cvalue (find_val valuation e2) in - Cvalue_forward.are_comparable Abstract_interp.Comp.Eq cval1 cval2 - && not (incompatible_zeros cval1 cval2) - - exception Top_location - - let find_loc valuation = fun lval -> - match Valuation.find_loc valuation lval with - | `Top -> raise Top_location - | `Value record -> record.loc - - let add_one_dep valuation lval deps = - match HCE.get lval with - | E _ -> assert false - | LV lv -> - let zone = - match lv with - | Var vi, NoOffset -> Locations.zone_of_varinfo vi - | _ -> - let expr = Cil.dummy_exp (Lval lv) in - Value_util.zone_of_expr (find_loc valuation) expr - in - Deps.add lval zone deps - - let add_deps valuation lvalues deps = - let deps = HCESet.fold (add_one_dep valuation) lvalues.read deps in - HCESet.fold (add_one_dep valuation) lvalues.addr deps - - let update _valuation state = `Value state - - let is_singleton = match get_cvalue with - | None -> fun _ -> false - | Some get -> - function - | `Bottom -> true - | `Value v -> Cvalue.V.cardinal_zero_or_one (get v) - - let expr_cardinal_zero_or_one valuation e = - match Valuation.find valuation e with - | `Top -> false (* should not happen *) - | `Value { value = { v } } -> is_singleton v - - let expr_is_cardinal_zero_or_one_loc valuation e = - match e.enode with - | Lval lv -> begin - let loc = Valuation.find_loc valuation lv in - match loc with - | `Top -> false (* should not happen *) - | `Value loc -> Precise_locs.cardinal_zero_or_one loc.loc - end - | _ -> false (* TODO: handle upcasts *) - - - let register expr valuation deps = - let term = HCE.of_exp expr in - if HCE.is_lval term - then - let deps = add_one_dep valuation term deps in - term, Hcexprs.empty_lvalues, deps - else - let lvalues = Hcexprs.syntactic_lvalues expr in - term, lvalues, add_deps valuation lvalues deps - - let indeterminate_copy = function - | Assign _ -> false - | Copy (_loc, value) -> not value.initialized || value.escaping - - (* Auxiliary function for [assign]. The equality is inferred, unless: - - some of the expressions involved are volatile - - the value has an aggregate type (as the current Eva values have no - meaning for such type, the equality would be useless or misleading). - - it is an assignment by copy, and the copied value is possibly - unitialized or escaping. In this case, when using the equality later, - the reevaluation of [right_expr] would reduce it incorrectly, by - removing indeterminate flags without emitting alarms. *) - let assign_eq left_lval right_expr value valuation state = - if Eval_typ.lval_contains_volatile left_lval || - Eval_typ.expr_contains_volatile right_expr || - not (Cil.isArithmeticOrPointerType (Cil.typeOfLval left_lval)) || - indeterminate_copy value - then state + Deps.add lval zone deps + + let add_deps valuation lvalues deps = + let deps = HCESet.fold (add_one_dep valuation) lvalues.read deps in + HCESet.fold (add_one_dep valuation) lvalues.addr deps + + let update _valuation state = `Value state + + let is_singleton = match get_cvalue with + | None -> fun _ -> false + | Some get -> + function + | `Bottom -> true + | `Value v -> Cvalue.V.cardinal_zero_or_one (get v) + + let expr_cardinal_zero_or_one valuation e = + match valuation.Abstract_domain.find e with + | `Top -> false (* should not happen *) + | `Value { value = { v } } -> is_singleton v + + let expr_is_cardinal_zero_or_one_loc valuation e = + match e.enode with + | Lval lv -> begin + let loc = valuation.Abstract_domain.find_loc lv in + match loc with + | `Top -> false (* should not happen *) + | `Value loc -> Precise_locs.cardinal_zero_or_one loc.loc + end + | _ -> false (* TODO: handle upcasts *) + + + let register expr valuation deps = + let term = HCE.of_exp expr in + if HCE.is_lval term + then + let deps = add_one_dep valuation term deps in + term, Hcexprs.empty_lvalues, deps + else + let lvalues = Hcexprs.syntactic_lvalues expr in + term, lvalues, add_deps valuation lvalues deps + + let indeterminate_copy = function + | Assign _ -> false + | Copy (_loc, value) -> not value.initialized || value.escaping + + (* Auxiliary function for [assign]. The equality is inferred, unless: + - some of the expressions involved are volatile + - the value has an aggregate type (as the current Eva values have no + meaning for such type, the equality would be useless or misleading). + - it is an assignment by copy, and the copied value is possibly + unitialized or escaping. In this case, when using the equality later, + the reevaluation of [right_expr] would reduce it incorrectly, by + removing indeterminate flags without emitting alarms. *) + let assign_eq left_lval right_expr value valuation state = + if Eval_typ.lval_contains_volatile left_lval || + Eval_typ.expr_contains_volatile right_expr || + not (Cil.isArithmeticOrPointerType (Cil.typeOfLval left_lval)) || + indeterminate_copy value + then state + else + let (equalities, deps, modified_zone: t) = state in + let lterm = HCE.of_lval left_lval in + let lterm_lvals = Hcexprs.empty_lvalues in + let deps = add_one_dep valuation lterm deps in + let rterm, rterm_lvals, deps = register right_expr valuation deps in + let equalities = + Equality.Set.unite + (lterm, lterm_lvals) (rterm, rterm_lvals) equalities + in + (equalities, deps, modified_zone: t) + + let assign _stmt left_value right_expr value valuation state = + let open Locations in + let left_loc = Precise_locs.imprecise_location left_value.lloc in + let direct_left_zone = Locations.(enumerate_valid_bits Write left_loc) in + let state = kill Hcexprs.Modified direct_left_zone state in + let right_expr = Cil.constFold true right_expr in + try + let indirect_left_zone = + Value_util.indirect_zone_of_lval (find_loc valuation) left_value.lval + and right_zone = + Value_util.zone_of_expr (find_loc valuation) right_expr + in + (* After an assignment lv = e, the equality [lv == eq] holds iff the value + of [e] and the location of [lv] are not modified by the assignment, + i.e. iff the dependencies of [e] and of the lhost and offset of [lv] + do not intersect the assigned location. + Moreover, the domain do not store the equality when the abstract + location of [lv] and the abstract value of [e] are singleton, as in + this case, the main cvalue domain is able to infer the equality. *) + if (Zone.intersects direct_left_zone right_zone) || + (Zone.intersects direct_left_zone indirect_left_zone) || + (is_singleton (Eval.value_assigned value) && + Locations.cardinal_zero_or_one left_loc) + then `Value state + else `Value (assign_eq left_value.lval right_expr value valuation state) + with Top_location -> `Value state + + (* Add the equalities between the formals of a function and the actuals + at the call. *) + let assign_formals valuation call state = + let assign_formal state arg = + if is_singleton (Eval.value_assigned arg.avalue) then + state else - let (equalities, deps, modified_zone: t) = state in - let lterm = HCE.of_lval left_lval in - let lterm_lvals = Hcexprs.empty_lvalues in - let deps = add_one_dep valuation lterm deps in - let rterm, rterm_lvals, deps = register right_expr valuation deps in - let equalities = - Equality.Set.unite - (lterm, lterm_lvals) (rterm, rterm_lvals) equalities - in - (equalities, deps, modified_zone: t) - - let assign _stmt left_value right_expr value valuation state = - let open Locations in - let left_loc = Precise_locs.imprecise_location left_value.lloc in - let direct_left_zone = Locations.(enumerate_valid_bits Write left_loc) in - let state = kill Hcexprs.Modified direct_left_zone state in - let right_expr = Cil.constFold true right_expr in - try - let indirect_left_zone = - Value_util.indirect_zone_of_lval (find_loc valuation) left_value.lval - and right_zone = - Value_util.zone_of_expr (find_loc valuation) right_expr - in - (* After an assignment lv = e, the equality [lv == eq] holds iff the value - of [e] and the location of [lv] are not modified by the assignment, - i.e. iff the dependencies of [e] and of the lhost and offset of [lv] - do not intersect the assigned location. - Moreover, the domain do not store the equality when the abstract - location of [lv] and the abstract value of [e] are singleton, as in - this case, the main cvalue domain is able to infer the equality. *) - if (Zone.intersects direct_left_zone right_zone) || - (Zone.intersects direct_left_zone indirect_left_zone) || - (is_singleton (Eval.value_assigned value) && - Locations.cardinal_zero_or_one left_loc) + try + let left_value = Var arg.formal, NoOffset in + assign_eq left_value arg.concrete arg.avalue valuation state + with Top_location -> state + in + List.fold_left assign_formal state call.arguments + + (* The domain infers equalities [e1 = e2] stemming from assignments, + meaning that e1 and e2 have not only the same value, but also the same + object representation — their values can thus be safely narrowed, + which is used by the domain to regain precision when possible. + The domain can also infer equalities from conditions, but C values + with different object representations may be equal, invalidating this + reasoning. This is the case for equalities between 0. and -0., and + between non-comparable pointers, so we need to skip such equalities. *) + let assume _stmt expr positive valuation (eqs, deps, modified_zone as state) = + match positive, expr.enode with + | true, BinOp (Eq, e1, e2, _) + | false, BinOp (Ne, e1, e2, _) -> + begin + if not (is_safe_equality valuation e1 e2) then `Value state - else `Value (assign_eq left_value.lval right_expr value valuation state) - with Top_location -> `Value state - - (* Add the equalities between the formals of a function and the actuals - at the call. *) - let assign_formals valuation call state = - let assign_formal state arg = - if is_singleton (Eval.value_assigned arg.avalue) then - state else - try - let left_value = Var arg.formal, NoOffset in - assign_eq left_value arg.concrete arg.avalue valuation state - with Top_location -> state - in - List.fold_left assign_formal state call.arguments - - (* The domain infers equalities [e1 = e2] stemming from assignments, - meaning that e1 and e2 have not only the same value, but also the same - object representation — their values can thus be safely narrowed, - which is used by the domain to regain precision when possible. - The domain can also infer equalities from conditions, but C values - with different object representations may be equal, invalidating this - reasoning. This is the case for equalities between 0. and -0., and - between non-comparable pointers, so we need to skip such equalities. *) - let assume _stmt expr positive valuation (eqs, deps, modified_zone as state) = - match positive, expr.enode with - | true, BinOp (Eq, e1, e2, _) - | false, BinOp (Ne, e1, e2, _) -> - begin - if not (is_safe_equality valuation e1 e2) + let e1 = Cil.constFold true e1 + and e2 = Cil.constFold true e2 in + if Eval_typ.expr_contains_volatile e1 + || Eval_typ.expr_contains_volatile e2 + || not (Cil.isArithmeticOrPointerType (Cil.typeOf e1)) + || (expr_is_cardinal_zero_or_one_loc valuation e1 && + expr_cardinal_zero_or_one valuation e2) + || (expr_is_cardinal_zero_or_one_loc valuation e2 && + expr_cardinal_zero_or_one valuation e1) then `Value state else - let e1 = Cil.constFold true e1 - and e2 = Cil.constFold true e2 in - if Eval_typ.expr_contains_volatile e1 - || Eval_typ.expr_contains_volatile e2 - || not (Cil.isArithmeticOrPointerType (Cil.typeOf e1)) - || (expr_is_cardinal_zero_or_one_loc valuation e1 && - expr_cardinal_zero_or_one valuation e2) - || (expr_is_cardinal_zero_or_one_loc valuation e2 && - expr_cardinal_zero_or_one valuation e1) - then `Value state - else - try - let a1, a1_lvals, deps = register e1 valuation deps in - let a2, a2_lvals, deps = register e2 valuation deps in - let eqs = Equality.Set.unite (a1, a1_lvals) (a2, a2_lvals) eqs in - `Value (eqs, deps, modified_zone) - with Top_location -> `Value state - end - | _ -> `Value state - - let start_call _stmt call valuation state = - let state = - match call_init_state call.kf with - | ISCaller -> assign_formals valuation call state - | ISFormals -> assign_formals valuation call empty - | ISEmpty -> empty - in - `Value state + try + let a1, a1_lvals, deps = register e1 valuation deps in + let a2, a2_lvals, deps = register e2 valuation deps in + let eqs = Equality.Set.unite (a1, a1_lvals) (a2, a2_lvals) eqs in + `Value (eqs, deps, modified_zone) + with Top_location -> `Value state + end + | _ -> `Value state + + let start_call _stmt call valuation state = + let state = + match call_init_state call.kf with + | ISCaller -> assign_formals valuation call state + | ISFormals -> assign_formals valuation call empty + | ISEmpty -> empty + in + `Value state - let finalize_call _stmt call ~pre ~post = - if call_init_state call.kf = ISCaller then - `Value post (* [pre] was the state inferred in the caller, and it - has been updated during the analysis of [kf] into - [post]. Send all the equalities back to the caller. *) - else - (* [pre] contains the equalities from the caller, but [post] was - computed starting from an essentially empty state. We must - restore the equalities of [pre]. *) - let (_, _, modif) = post in - (* Invalidate the equalities that are no longer true. *) - let pre' = kill Hcexprs.Modified modif pre in - (* then merge the two sets of equalities *) - `Value (concat pre' post) - - let show_expr _valuation (equalities, _, _) fmt expr = - let atom = Hcexprs.HCE.of_exp expr in - match Equality.Set.find_option atom equalities with - | Some equality -> Equality.Equality.pretty fmt equality - | None -> () - end + let finalize_call _stmt call ~pre ~post = + if call_init_state call.kf = ISCaller then + `Value post (* [pre] was the state inferred in the caller, and it + has been updated during the analysis of [kf] into + [post]. Send all the equalities back to the caller. *) + else + (* [pre] contains the equalities from the caller, but [post] was + computed starting from an essentially empty state. We must + restore the equalities of [pre]. *) + let (_, _, modif) = post in + (* Invalidate the equalities that are no longer true. *) + let pre' = kill Hcexprs.Modified modif pre in + (* then merge the two sets of equalities *) + `Value (concat pre' post) + + let show_expr _valuation (equalities, _, _) fmt expr = + let atom = Hcexprs.HCE.of_exp expr in + match Equality.Set.find_option atom equalities with + | Some equality -> Equality.Equality.pretty fmt equality + | None -> () let logic_assign _assigns location ~pre:_ state = let loc = Precise_locs.imprecise_location location in diff --git a/src/plugins/value/domains/gauges/gauges_domain.ml b/src/plugins/value/domains/gauges/gauges_domain.ml index 62ce07b51b9..5c892c9e225 100644 --- a/src/plugins/value/domains/gauges/gauges_domain.ml +++ b/src/plugins/value/domains/gauges/gauges_domain.ml @@ -1130,6 +1130,7 @@ module D_Impl : Abstract_domain.S type value = Cvalue.V.t type state = G.t type location = Precise_locs.precise_location + type origin = unit include G @@ -1151,8 +1152,6 @@ module D_Impl : Abstract_domain.S (* reverts implicitly to Top *) remove_variables vars state - type origin = unit - let kill loc state = let loc = Precise_locs.imprecise_location loc in let loc = loc.Locations.loc in @@ -1163,108 +1162,96 @@ module D_Impl : Abstract_domain.S let vars = Locations.Location_Bits.fold_topset_ok aux_base loc [] in remove_variables vars state - module Transfer (Valuation: - Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = location) - : Abstract_domain.Transfer - with type state := state - and type value := value - and type location := location - and type valuation := Valuation.t - = struct - - let assume_exp valuation e r state = - if r.reductness = Created || r.reductness = Reduced then - match e.enode with - | Lval lv -> begin - match Valuation.find_loc valuation lv with - | `Top -> `Value state - | `Value {loc} -> - let loc = Precise_locs.imprecise_location loc in - try - let b = loc_to_base loc (Cil.typeOfLval lv) in - match r.value.v with - | `Bottom -> `Value state - | `Value v -> - match backward_loop state b v with - | Some `Bottom -> `Bottom - | Some (`Value _ as s) -> s - | None -> `Value state - with Untranslatable -> `Value state - end - | _ -> `Value state - else `Value state - - let assume_exp_bot valuation e r state = - state >>- assume_exp valuation e r - - let update valuation state = - let assume_one = assume_exp_bot valuation in - Valuation.fold assume_one valuation (`Value state) - - let assume _ _ _ = update - - exception Unassignable - - let assign _kinstr lv e _assignment valuation (state:state) = - update valuation state >>- fun state -> - let to_loc lv = - match Valuation.find_loc valuation lv with - | `Value r -> Precise_locs.imprecise_location r.loc - | `Top -> raise Unassignable - in - let to_val e = - match Valuation.find valuation e with - | `Top -> raise Unassignable - | `Value v -> - match v.value.initialized, v.value.escaping, v.value.v with - | true, false, `Value v -> v - | _ -> raise Unassignable - in - try `Value (G.assign to_loc to_val lv.lval e state) - with Unassignable -> `Value (kill lv.lloc state) - - let finalize_call _stmt _call ~pre ~post = - let state = - match function_calls_handling with - | FullInterprocedural -> post - | IntraproceduralNonReferenced -> pre - | IntraproceduralAll -> pre (* unsound here *) - in - `Value state - - let start_call _stmt call valuation state = - let state = - match function_calls_handling with - | FullInterprocedural -> update valuation state - | IntraproceduralAll - | IntraproceduralNonReferenced -> `Value G.empty - in - state >>- fun state -> - (* track [arg.formal] into [state]. Important for functions that - receive a size as argument. *) - let aux_arg state arg = - try - let vi = arg.formal in - if not (tracked_variable vi) then raise Untranslatable; - let b = Base.of_varinfo vi in - let v = match arg.avalue with - | Assign v -> v - | Copy (_, v) -> - match v.initialized, v.escaping, v.v with - | true, false, `Value v -> v - | _ -> raise Untranslatable - in - let g = Gauge.ct v in - store_gauge b g state - with Untranslatable -> state - in - let state = List.fold_left aux_arg state call.arguments in - `Value state + let assume_exp valuation e r state = + if r.reductness = Created || r.reductness = Reduced then + match e.enode with + | Lval lv -> begin + match valuation.Abstract_domain.find_loc lv with + | `Top -> `Value state + | `Value {loc} -> + let loc = Precise_locs.imprecise_location loc in + try + let b = loc_to_base loc (Cil.typeOfLval lv) in + match r.value.v with + | `Bottom -> `Value state + | `Value v -> + match backward_loop state b v with + | Some `Bottom -> `Bottom + | Some (`Value _ as s) -> s + | None -> `Value state + with Untranslatable -> `Value state + end + | _ -> `Value state + else `Value state + + let assume_exp_bot valuation e r state = + state >>- assume_exp valuation e r + + let update valuation state = + let assume_one = assume_exp_bot valuation in + valuation.Abstract_domain.fold assume_one (`Value state) + + let assume _ _ _ = update + + exception Unassignable + + let assign _kinstr lv e _assignment valuation (state:state) = + update valuation state >>- fun state -> + let to_loc lv = + match valuation.Abstract_domain.find_loc lv with + | `Value r -> Precise_locs.imprecise_location r.loc + | `Top -> raise Unassignable + in + let to_val e = + match valuation.Abstract_domain.find e with + | `Top -> raise Unassignable + | `Value v -> + match v.value.initialized, v.value.escaping, v.value.v with + | true, false, `Value v -> v + | _ -> raise Unassignable + in + try `Value (G.assign to_loc to_val lv.lval e state) + with Unassignable -> `Value (kill lv.lloc state) + + let finalize_call _stmt _call ~pre ~post = + let state = + match function_calls_handling with + | FullInterprocedural -> post + | IntraproceduralNonReferenced -> pre + | IntraproceduralAll -> pre (* unsound here *) + in + `Value state + + let start_call _stmt call valuation state = + let state = + match function_calls_handling with + | FullInterprocedural -> update valuation state + | IntraproceduralAll + | IntraproceduralNonReferenced -> `Value G.empty + in + state >>- fun state -> + (* track [arg.formal] into [state]. Important for functions that + receive a size as argument. *) + let aux_arg state arg = + try + let vi = arg.formal in + if not (tracked_variable vi) then raise Untranslatable; + let b = Base.of_varinfo vi in + let v = match arg.avalue with + | Assign v -> v + | Copy (_, v) -> + match v.initialized, v.escaping, v.v with + | true, false, `Value v -> v + | _ -> raise Untranslatable + in + let g = Gauge.ct v in + store_gauge b g state + with Untranslatable -> state + in + let state = List.fold_left aux_arg state call.arguments in + `Value state - let show_expr _valuation _state _fmt _expr = () - end + let show_expr _valuation _state _fmt _expr = () let enter_loop = G.enter_loop let incr_loop_counter _ = G.inc diff --git a/src/plugins/value/domains/inout_domain.ml b/src/plugins/value/domains/inout_domain.ml index 1a5a4888c93..b34535306a3 100644 --- a/src/plugins/value/domains/inout_domain.ml +++ b/src/plugins/value/domains/inout_domain.ml @@ -213,6 +213,7 @@ module Internal type state = inout type value = Cvalue.V.t type location = Precise_locs.precise_location + type origin = unit include (LatticeInout: sig include Datatype.S_with_collections with type t = state @@ -225,47 +226,29 @@ module Internal let enter_scope _kf _vars state = state let leave_scope _kf vars state = Transfer.remove_variables vars state - type origin = unit + let to_z valuation lv = + match valuation.Abstract_domain.find_loc lv with + | `Value loc -> loc.Eval.loc + | `Top -> Precise_locs.loc_top (* should not occur *) + + let assign _ki lv e _v valuation state = + let to_z = to_z valuation in + let effects = Transfer.effects_assign to_z lv e in + `Value (Transfer.catenate state effects) + + let assume _stmt e _pos valuation state = + let to_z = to_z valuation in + let effects = Transfer.effects_assume to_z e in + `Value (Transfer.catenate state effects) + + let start_call _stmt _call _valuation _state = `Value LatticeInout.empty + + let finalize_call _stmt _call ~pre ~post = + `Value (Transfer.catenate pre post) + + let update _valuation state = `Value state - module Transfer (Valuation: Abstract_domain.Valuation - with type value = value - and type origin = origin - and type loc = Precise_locs.precise_location) - : Abstract_domain.Transfer - with type state = state - and type value = Cvalue.V.t - and type location = Precise_locs.precise_location - and type valuation = Valuation.t - = struct - type value = Cvalue.V.t - type state = inout - type location = Precise_locs.precise_location - type valuation = Valuation.t - - let to_z valuation lv = - match Valuation.find_loc valuation lv with - | `Value loc -> loc.Eval.loc - | `Top -> Precise_locs.loc_top (* should not occur *) - - let assign _ki lv e _v valuation state = - let to_z = to_z valuation in - let effects = Transfer.effects_assign to_z lv e in - `Value (Transfer.catenate state effects) - - let assume _stmt e _pos valuation state = - let to_z = to_z valuation in - let effects = Transfer.effects_assume to_z e in - `Value (Transfer.catenate state effects) - - let start_call _stmt _call _valuation _state = `Value LatticeInout.empty - - let finalize_call _stmt _call ~pre ~post = - `Value (Transfer.catenate pre post) - - let update _valuation state = `Value state - - let show_expr _valuation _state _fmt _expr = () - end + let show_expr _valuation _state _fmt _expr = () (* Memexec *) let relate _kf _bases _state = Base.SetLattice.empty diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml index c5a14838419..fae7d982ebc 100644 --- a/src/plugins/value/domains/octagons.ml +++ b/src/plugins/value/domains/octagons.ml @@ -1037,8 +1037,8 @@ module Domain = struct type value = Cvalue.V.t type location = Precise_locs.precise_location - type origin = unit + let top_value = `Value (Cvalue.V.top, ()), Alarmset.all let extract_expr oracle state expr = @@ -1108,138 +1108,132 @@ module Domain = struct let state = Zone.fold_bases kill_base zone state in { state with modified } - module Transfer - (Valuation: Abstract_domain.Valuation with type value = value - and type loc = location) - = struct - - (* Evaluation function of expressions to ival, from a [valuation]. *) - let evaluation_function valuation = fun expr -> - match Valuation.find valuation expr with - | `Top -> `Top - | `Value record -> - match record.Eval.value.v with - | `Bottom -> `Top (* TODO: why this keeps happening? *) - | `Value cvalue -> - try `Value (Cvalue.V.project_ival cvalue) - with Cvalue.V.Not_based_on_null -> `Top - - exception EBottom - - let infer_octagons evaluate expr ival state = - let octagons = Rewriting.make_octagons evaluate expr ival in - let add_octagon state octagon = - match State.add_octagon state octagon with - | `Bottom -> raise EBottom - | `Value state -> state - in - List.fold_left add_octagon state octagons + (* Evaluation function of expressions to ival, from a [valuation]. *) + let evaluation_function valuation = fun expr -> + match valuation.Abstract_domain.find expr with + | `Top -> `Top + | `Value record -> + match record.Eval.value.v with + | `Bottom -> `Top (* TODO: why this keeps happening? *) + | `Value cvalue -> + try `Value (Cvalue.V.project_ival cvalue) + with Cvalue.V.Not_based_on_null -> `Top - let infer_interval expr ival state = - if not infer_intervals - then state - else - match expr.enode with - | Lval (Var varinfo, NoOffset) - when Cil.isIntegralType varinfo.vtype -> - let intervals = Intervals.add varinfo ival state.intervals in - { state with intervals } - | _ -> state - - let update valuation state = - let evaluate = evaluation_function valuation in - let aux expr record state = - let value = record.Eval.value in - match record.reductness, value.v, value.initialized, value.escaping with - | (Created | Reduced), `Value cvalue, true, false -> - begin - try - let ival = Cvalue.V.project_ival cvalue in - let state = infer_octagons evaluate expr ival state in - infer_interval expr ival state - with Cvalue.V.Not_based_on_null -> state - end - | _ -> state - in - try `Value (check "update" (Valuation.fold aux valuation state)) - with EBottom -> `Bottom + exception EBottom - let assign_interval varinfo assigned state = - if not infer_intervals - then state - else - match assigned with - | Assign v - | Copy (_, { v = `Value v; initialized = true; escaping = false }) -> - begin - try - let ival = Cvalue.V.project_ival v in - let intervals = Intervals.add varinfo ival state.intervals in - { state with intervals } - with Cvalue.V.Not_based_on_null -> state - end - | _ -> state - - let assign_variable varinfo expr assigned valuation state = - let evaluate = evaluation_function valuation in - (* TODO: redundant with rewrite_binop below. *) - let vars = Rewriting.rewrite evaluate expr in - let equal_varinfo v = Variable.equal varinfo v.Rewriting.varinfo in - let state = - try - let var = List.find equal_varinfo vars in - let inverse = not var.Rewriting.sign in - State.sub_delta ~inverse state varinfo var.Rewriting.coeff - with Not_found -> State.remove state varinfo - in - let state = assign_interval varinfo assigned state in - let enode = Lval (Var varinfo, NoOffset) in - let left_expr = Cil.new_exp ~loc:expr.eloc enode in - (* On the assignment X = E; if X-E can be rewritten as ±(X±Y-v), - then the octagonal constraint [X±Y ∈ v] holds. *) - let octagons = Rewriting.rewrite_binop evaluate left_expr Sub expr in - let state = - List.fold_left - (fun acc (_sign, octagon) -> - acc >>- fun state -> State.add_octagon state octagon) - (`Value state) octagons + let infer_octagons evaluate expr ival state = + let octagons = Rewriting.make_octagons evaluate expr ival in + let add_octagon state octagon = + match State.add_octagon state octagon with + | `Bottom -> raise EBottom + | `Value state -> state + in + List.fold_left add_octagon state octagons + + let infer_interval expr ival state = + if not infer_intervals + then state + else + match expr.enode with + | Lval (Var varinfo, NoOffset) + when Cil.isIntegralType varinfo.vtype -> + let intervals = Intervals.add varinfo ival state.intervals in + { state with intervals } + | _ -> state + + let update valuation state = + let evaluate = evaluation_function valuation in + let aux expr record state = + let value = record.Eval.value in + match record.reductness, value.v, value.initialized, value.escaping with + | (Created | Reduced), `Value cvalue, true, false -> + begin + try + let ival = Cvalue.V.project_ival cvalue in + let state = infer_octagons evaluate expr ival state in + infer_interval expr ival state + with Cvalue.V.Not_based_on_null -> state + end + | _ -> state + in + try `Value (check "update" (valuation.Abstract_domain.fold aux state)) + with EBottom -> `Bottom + + let assign_interval varinfo assigned state = + if not infer_intervals + then state + else + match assigned with + | Assign v + | Copy (_, { v = `Value v; initialized = true; escaping = false }) -> + begin + try + let ival = Cvalue.V.project_ival v in + let intervals = Intervals.add varinfo ival state.intervals in + { state with intervals } + with Cvalue.V.Not_based_on_null -> state + end + | _ -> state + + let assign_variable varinfo expr assigned valuation state = + let evaluate = evaluation_function valuation in + (* TODO: redundant with rewrite_binop below. *) + let vars = Rewriting.rewrite evaluate expr in + let equal_varinfo v = Variable.equal varinfo v.Rewriting.varinfo in + let state = + try + let var = List.find equal_varinfo vars in + let inverse = not var.Rewriting.sign in + State.sub_delta ~inverse state varinfo var.Rewriting.coeff + with Not_found -> State.remove state varinfo + in + let state = assign_interval varinfo assigned state in + let enode = Lval (Var varinfo, NoOffset) in + let left_expr = Cil.new_exp ~loc:expr.eloc enode in + (* On the assignment X = E; if X-E can be rewritten as ±(X±Y-v), + then the octagonal constraint [X±Y ∈ v] holds. *) + let octagons = Rewriting.rewrite_binop evaluate left_expr Sub expr in + let state = + List.fold_left + (fun acc (_sign, octagon) -> + acc >>- fun state -> State.add_octagon state octagon) + (`Value state) octagons + in + state >>-: check "precise assign" + + let assign _kinstr left_value expr assigned valuation state = + update valuation state >>- fun state -> + match left_value.lval with + | Var varinfo, NoOffset when Cil.isIntegralType varinfo.vtype -> + assign_variable varinfo expr assigned valuation state + | _ -> + let written_loc = Precise_locs.imprecise_location left_value.lloc in + let written_zone = + Locations.(enumerate_valid_bits Write written_loc) in - state >>-: check "precise assign" - - let assign _kinstr left_value expr assigned valuation state = - update valuation state >>- fun state -> - match left_value.lval with - | Var varinfo, NoOffset when Cil.isIntegralType varinfo.vtype -> - assign_variable varinfo expr assigned valuation state - | _ -> - let written_loc = Precise_locs.imprecise_location left_value.lloc in - let written_zone = - Locations.(enumerate_valid_bits Write written_loc) - in - let state = kill written_zone state in - `Value (check "imprecise assign" state) + let state = kill written_zone state in + `Value (check "imprecise assign" state) - let assume _stmt _exp _bool = update + let assume _stmt _exp _bool = update - let start_call _stmt call valuation state = - if intraprocedural () - then `Value (empty ()) - else - let state = { state with modified = Locations.Zone.bottom } in - let assign_formal state { formal; concrete; avalue } = - state >>- assign_variable formal concrete avalue valuation - in - List.fold_left assign_formal (`Value state) call.arguments + let start_call _stmt call valuation state = + if intraprocedural () + then `Value (empty ()) + else + let state = { state with modified = Locations.Zone.bottom } in + let assign_formal state { formal; concrete; avalue } = + state >>- assign_variable formal concrete avalue valuation + in + List.fold_left assign_formal (`Value state) call.arguments - let finalize_call _stmt _call ~pre ~post = - if intraprocedural () - then `Value (kill post.modified pre) - else - let modified = Locations.Zone.join post.modified pre.modified in - `Value { post with modified } + let finalize_call _stmt _call ~pre ~post = + if intraprocedural () + then `Value (kill post.modified pre) + else + let modified = Locations.Zone.join post.modified pre.modified in + `Value { post with modified } - let show_expr _valuation _state _fmt _expr = () - end + let show_expr _valuation _state _fmt _expr = () let logic_assign _logic_assign location ~pre:_ state = let loc = Precise_locs.imprecise_location location in diff --git a/src/plugins/value/domains/offsm_domain.ml b/src/plugins/value/domains/offsm_domain.ml index f491384582b..2809ade3ffa 100644 --- a/src/plugins/value/domains/offsm_domain.ml +++ b/src/plugins/value/domains/offsm_domain.ml @@ -97,6 +97,8 @@ module Internal : Domain_builder.InputDomain type value = offsm_or_top type state = Memory.t type location = Precise_locs.precise_location + type origin = unit (* ???? *) + include (Memory: sig include Datatype.S_with_collections with type t = state include Abstract_domain.Lattice with type state := state @@ -115,65 +117,51 @@ module Internal : Domain_builder.InputDomain let incr_loop_counter _ state = state let leave_loop _ state = state - type origin = unit (* ???? *) - let kill loc state = Memory.add_binding ~exact:true state loc V_Or_Uninitialized.top - module Transfer (Valuation: - Abstract_domain.Valuation with type value = value - and type origin = origin - and type loc = Precise_locs.precise_location) - : Abstract_domain.Transfer - with type state := state - and type value := offsm_or_top - and type location := Precise_locs.precise_location - and type valuation := Valuation.t - = struct - - let update _valuation st = `Value st (* TODO? *) - - let store loc state v = - let state' = - match v with - | Top -> kill loc state - | O o -> - if not store_redundant && V_Offsetmap.is_single_interval o then - kill loc state - else - match loc.Locations.size with - | Int_Base.Top -> assert false - | Int_Base.Value size -> - Memory.paste_offsetmap - ~from:o ~dst_loc:loc.Locations.loc ~size ~exact:true state - in - match state' with - | Memory.Bottom -> `Bottom - | _ -> `Value state' - - let generic_assign lv value state = - let loc = Precise_locs.imprecise_location lv.lloc in - let v = Eval.value_assigned value in - let v = match v with - | `Value v -> v - (* Copy of fully indeterminate bits. We could store an uninitialized - bottom, or something like that. Since this would be redundant - with the legacy domain, we just drop the value. *) - | `Bottom -> Top - in - store loc state v - - let assign _kinstr lv _e assignment _valuation state = - generic_assign lv assignment state - - let assume _ _ _ _ state = `Value state - - let finalize_call _stmt _call ~pre:_ ~post = `Value post - - let start_call _stmt _call valuation state = update valuation state - - let show_expr _valuation _state _fmt _expr = () - end + let update _valuation st = `Value st (* TODO? *) + + let store loc state v = + let state' = + match v with + | Top -> kill loc state + | O o -> + if not store_redundant && V_Offsetmap.is_single_interval o then + kill loc state + else + match loc.Locations.size with + | Int_Base.Top -> assert false + | Int_Base.Value size -> + Memory.paste_offsetmap + ~from:o ~dst_loc:loc.Locations.loc ~size ~exact:true state + in + match state' with + | Memory.Bottom -> `Bottom + | _ -> `Value state' + + let generic_assign lv value state = + let loc = Precise_locs.imprecise_location lv.lloc in + let v = Eval.value_assigned value in + let v = match v with + | `Value v -> v + (* Copy of fully indeterminate bits. We could store an uninitialized + bottom, or something like that. Since this would be redundant + with the legacy domain, we just drop the value. *) + | `Bottom -> Top + in + store loc state v + + let assign _kinstr lv _e assignment _valuation state = + generic_assign lv assignment state + + let assume _ _ _ _ state = `Value state + + let finalize_call _stmt _call ~pre:_ ~post = `Value post + + let start_call _stmt _call valuation state = update valuation state + + let show_expr _valuation _state _fmt _expr = () let extract_expr _oracle _state _exp = `Value (Offsm_value.Offsm.top, ()), Alarmset.all diff --git a/src/plugins/value/domains/simple_memory.ml b/src/plugins/value/domains/simple_memory.ml index ea175768582..97eed460437 100644 --- a/src/plugins/value/domains/simple_memory.ml +++ b/src/plugins/value/domains/simple_memory.ml @@ -187,6 +187,7 @@ module Make_Internal (Info: sig val name: string end) (Value: Value) = struct type state = t type value = Value.t type location = Precise_locs.precise_location + type origin = unit let log_category = Value_parameters.register_category ("d-" ^ Info.name) @@ -207,99 +208,90 @@ module Make_Internal (Info: sig val name: string end) (Value: Value) = struct let reduce_further _state _expr _value = [] - type origin = unit - - module Transfer - (Valuation: Abstract_domain.Valuation with type value := value - and type origin := origin - and type loc := location) - = struct - - (* This function binds [loc] to [v], of type [typ], in [state]. - [v] can be [`Bottom], which means that its contents are guaranteed - to be indeterminate (e.g. unitialized data). *) - let bind_loc loc typ v state = - match v with - (* We are adding a "good" value. Store it in the state. *) - | `Value v -> add loc typ v state - (* Indeterminate value. Drop the information known for loc. *) - | `Bottom -> remove loc state - - (* This function updates [state] with information for [expr], only possible - when it is an lvalue. In this case, we can update the corresponding - location with the result of the evaluation of [exp]. Both the value and - the location are found in the [valuation]. *) - let assume_exp valuation expr record state = - match expr.enode with - | Lval lv -> begin - match Valuation.find_loc valuation lv with - | `Top -> state - | `Value {loc; typ} -> - if Precise_locs.cardinal_zero_or_one loc - then bind_loc loc typ record.value.v state - else state - end - | _ -> state - - (* This function fills [state] according to the information available - in [valuation]. This information is computed by Eva's engine for - all the expressions involved in the current statement. *) - let assume_valuation valuation state = - Valuation.fold (assume_exp valuation) valuation state - - (* Abstraction of an assignment. *) - let assign _kinstr lv _expr value valuation state = - (* Update the state with the information obtained from evaluating - [lv] and [e] *) - let state = assume_valuation valuation state in - (* Extract the abstract value *) - let value = Eval.value_assigned value in - (* Store the information [lv = e;] in the state *) - let state = bind_loc lv.lloc lv.ltyp value state in - `Value state - - let update valuation state = `Value (assume_valuation valuation state) - - (* Abstraction of a conditional. All information inferred by the engine - is present in the valuation, and must be stored in the memory - abstraction of the domain itself. *) - let assume _stmt _expr _pos = update - - let start_call _stmt call _valuation state = - let bind_argument state argument = - let typ = argument.formal.vtype in - let loc = Main_locations.PLoc.eval_varinfo argument.formal in - let value = Eval.value_assigned argument.avalue in - bind_loc loc typ value state - in - let state = List.fold_left bind_argument state call.arguments in - `Value state - - let finalize_call _stmt call ~pre:_ ~post = - let kf_name = Kernel_function.get_name call.kf in - match find_builtin kf_name, call.return with - | None, _ | _, None -> `Value post - | Some f, Some return -> - let extract_value arg = Eval.value_assigned arg.avalue in - let args = List.map extract_value call.arguments in - if List.exists (function `Bottom -> true | `Value _ -> false) args - then `Bottom - else - let args = List.map Bottom.non_bottom args in - f args >>-: fun result -> - let return_loc = Main_locations.PLoc.eval_varinfo return in - bind_loc return_loc return.vtype (`Value result) post - - let show_expr valuation state fmt expr = - match expr.enode with - | Lval lval -> - begin - match Valuation.find_loc valuation lval with - | `Top -> () - | `Value {loc; typ} -> Value.pretty fmt (find loc typ state) - end - | _ -> () - end + (* This function binds [loc] to [v], of type [typ], in [state]. + [v] can be [`Bottom], which means that its contents are guaranteed + to be indeterminate (e.g. unitialized data). *) + let bind_loc loc typ v state = + match v with + (* We are adding a "good" value. Store it in the state. *) + | `Value v -> add loc typ v state + (* Indeterminate value. Drop the information known for loc. *) + | `Bottom -> remove loc state + + (* This function updates [state] with information for [expr], only possible + when it is an lvalue. In this case, we can update the corresponding + location with the result of the evaluation of [exp]. Both the value and + the location are found in the [valuation]. *) + let assume_exp valuation expr record state = + match expr.enode with + | Lval lv -> begin + match valuation.Abstract_domain.find_loc lv with + | `Top -> state + | `Value {loc; typ} -> + if Precise_locs.cardinal_zero_or_one loc + then bind_loc loc typ record.value.v state + else state + end + | _ -> state + + (* This function fills [state] according to the information available + in [valuation]. This information is computed by Eva's engine for + all the expressions involved in the current statement. *) + let assume_valuation valuation state = + valuation.Abstract_domain.fold (assume_exp valuation) state + + (* Abstraction of an assignment. *) + let assign _kinstr lv _expr value valuation state = + (* Update the state with the information obtained from evaluating + [lv] and [e] *) + let state = assume_valuation valuation state in + (* Extract the abstract value *) + let value = Eval.value_assigned value in + (* Store the information [lv = e;] in the state *) + let state = bind_loc lv.lloc lv.ltyp value state in + `Value state + + let update valuation state = `Value (assume_valuation valuation state) + + (* Abstraction of a conditional. All information inferred by the engine + is present in the valuation, and must be stored in the memory + abstraction of the domain itself. *) + let assume _stmt _expr _pos = update + + let start_call _stmt call _valuation state = + let bind_argument state argument = + let typ = argument.formal.vtype in + let loc = Main_locations.PLoc.eval_varinfo argument.formal in + let value = Eval.value_assigned argument.avalue in + bind_loc loc typ value state + in + let state = List.fold_left bind_argument state call.arguments in + `Value state + + let finalize_call _stmt call ~pre:_ ~post = + let kf_name = Kernel_function.get_name call.kf in + match find_builtin kf_name, call.return with + | None, _ | _, None -> `Value post + | Some f, Some return -> + let extract_value arg = Eval.value_assigned arg.avalue in + let args = List.map extract_value call.arguments in + if List.exists (function `Bottom -> true | `Value _ -> false) args + then `Bottom + else + let args = List.map Bottom.non_bottom args in + f args >>-: fun result -> + let return_loc = Main_locations.PLoc.eval_varinfo return in + bind_loc return_loc return.vtype (`Value result) post + + let show_expr valuation state fmt expr = + match expr.enode with + | Lval lval -> + begin + match valuation.Abstract_domain.find_loc lval with + | `Top -> () + | `Value {loc; typ} -> Value.pretty fmt (find loc typ state) + end + | _ -> () let enter_scope _kf _vars state = state let leave_scope _kf vars state = remove_variables vars state diff --git a/src/plugins/value/domains/symbolic_locs.ml b/src/plugins/value/domains/symbolic_locs.ml index 00220370476..f325e2a7148 100644 --- a/src/plugins/value/domains/symbolic_locs.ml +++ b/src/plugins/value/domains/symbolic_locs.ml @@ -466,6 +466,8 @@ module Internal : Domain_builder.InputDomain type state = Memory.t type value = V.t type location = Precise_locs.precise_location + type origin = unit + include (Memory: sig include Datatype.S_with_collections with type t = state include Abstract_domain.Lattice with type state := state @@ -485,101 +487,87 @@ module Internal : Domain_builder.InputDomain let incr_loop_counter _ state = state let leave_loop _ state = state - type origin = unit - - module Transfer (Valuation: Abstract_domain.Valuation - with type value = value - and type origin = origin - and type loc = Precise_locs.precise_location) - : Abstract_domain.Transfer - with type state := state - and type value := V.t - and type location := Precise_locs.precise_location - and type valuation := Valuation.t - = struct - - (* build a [get_locs] function from a valuation *) - let get_locs valuation = - fun lv -> - let r = - match Valuation.find_loc valuation lv with - | `Top -> Precise_locs.loc_top - | `Value loc -> loc.Eval.loc - in - if Precise_locs.(equal_loc loc_top r) then - Value_parameters.fatal "Unknown location for %a" Printer.pp_lval lv - else r - - (* update the state according to the information known in the valuation. - Important, because on statements such as [if (t[i] + j <= 3)], the - interesting information on [t[i]] is only in the valuation. *) - let update valuation state = - let aux e r state = - let v = r.value in - (* TODO: incorporate DB criterion: only expressions that are immediate - lvalues, or that embed two non-singleton lvalues for the first - time. *) - match r.reductness, v.v, v.initialized, v.escaping with - | (Created | Reduced), `Value v, true, false -> - if not (is_cond e) && multiple_loc_exp (get_locs valuation) e then - begin - let k = K.HCE.of_exp e in - (* remove the existing binding: the key may already be in - the state, and [add_exp] assumes it is not the case. - The new dependencies may not be the same (in rare cases - where one dependency has disappeared by reduction), so - we need to update the dependency inverse maps. *) - (* TODO: it would be more efficient to use a function that - compares the previous and current dependencies, and update - the inverse maps accordingly. *) - let state = Memory.remove_key k state in - Memory.add_exp state (get_locs valuation) e v - end - else - state - | _ -> state - in - `Value (Valuation.fold aux valuation state) - - let store_value valuation lv loc state v = - let loc = Precise_locs.imprecise_location loc in - (* Remove the keys that are overwritten because [loc] is written *) - let state = Memory.kill loc state in - if Locations.cardinal_zero_or_one loc then - (* Stored by the standard domain. Skip *) - `Value state - else - (* Add the new binding *) - `Value (Memory.add_lv state (get_locs valuation) lv v) - - (* Assume we may be copying indeterminate bits. Kill existing information *) - let store_indeterminate state loc = - let loc = Precise_locs.imprecise_location loc in - `Value (Memory.kill loc state) - - let store_copy valuation lv loc state fv = - if Cil.isArithmeticOrPointerType lv.ltyp then - match fv.v, fv.initialized, fv.escaping with - | `Value v, true, false -> store_value valuation lv.lval loc state v - | _ -> store_indeterminate state loc - else - store_indeterminate state loc + (* build a [get_locs] function from a valuation *) + let get_locs valuation = + fun lv -> + let r = + match valuation.Abstract_domain.find_loc lv with + | `Top -> Precise_locs.loc_top + | `Value loc -> loc.Eval.loc + in + if Precise_locs.(equal_loc loc_top r) then + Value_parameters.fatal "Unknown location for %a" Printer.pp_lval lv + else r + + (* update the state according to the information known in the valuation. + Important, because on statements such as [if (t[i] + j <= 3)], the + interesting information on [t[i]] is only in the valuation. *) + let update valuation state = + let aux e r state = + let v = r.value in + (* TODO: incorporate DB criterion: only expressions that are immediate + lvalues, or that embed two non-singleton lvalues for the first + time. *) + match r.reductness, v.v, v.initialized, v.escaping with + | (Created | Reduced), `Value v, true, false -> + if not (is_cond e) && multiple_loc_exp (get_locs valuation) e then + begin + let k = K.HCE.of_exp e in + (* remove the existing binding: the key may already be in + the state, and [add_exp] assumes it is not the case. + The new dependencies may not be the same (in rare cases + where one dependency has disappeared by reduction), so + we need to update the dependency inverse maps. *) + (* TODO: it would be more efficient to use a function that + compares the previous and current dependencies, and update + the inverse maps accordingly. *) + let state = Memory.remove_key k state in + Memory.add_exp state (get_locs valuation) e v + end + else + state + | _ -> state + in + `Value (valuation.Abstract_domain.fold aux state) + + let store_value valuation lv loc state v = + let loc = Precise_locs.imprecise_location loc in + (* Remove the keys that are overwritten because [loc] is written *) + let state = Memory.kill loc state in + if Locations.cardinal_zero_or_one loc then + (* Stored by the standard domain. Skip *) + `Value state + else + (* Add the new binding *) + `Value (Memory.add_lv state (get_locs valuation) lv v) + + (* Assume we may be copying indeterminate bits. Kill existing information *) + let store_indeterminate state loc = + let loc = Precise_locs.imprecise_location loc in + `Value (Memory.kill loc state) + + let store_copy valuation lv loc state fv = + if Cil.isArithmeticOrPointerType lv.ltyp then + match fv.v, fv.initialized, fv.escaping with + | `Value v, true, false -> store_value valuation lv.lval loc state v + | _ -> store_indeterminate state loc + else + store_indeterminate state loc - (* perform [lv = e] in [state] *) - let assign _kinstr lv _e v valuation state = - update valuation state >>- fun state -> - match v with - | Copy (_, vc) -> store_copy valuation lv lv.lloc state vc - | Assign v -> store_value valuation lv.lval lv.lloc state v + (* perform [lv = e] in [state] *) + let assign _kinstr lv _e v valuation state = + update valuation state >>- fun state -> + match v with + | Copy (_, vc) -> store_copy valuation lv lv.lloc state vc + | Assign v -> store_value valuation lv.lval lv.lloc state v - let assume _stmt _exp _pos valuation state = update valuation state + let assume _stmt _exp _pos valuation state = update valuation state - let start_call _stmt _call valuation state = update valuation state + let start_call _stmt _call valuation state = update valuation state - let finalize_call _stmt _call ~pre:_ ~post = `Value post + let finalize_call _stmt _call ~pre:_ ~post = `Value post - let show_expr _valuation _state _fmt _expr = () - end + let show_expr _valuation _state _fmt _expr = () let top_query = `Value (V.top, ()), Alarmset.all diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml index c22c8bcfd19..771c2220a59 100644 --- a/src/plugins/value/domains/traces_domain.ml +++ b/src/plugins/value/domains/traces_domain.ml @@ -848,6 +848,7 @@ module Internal = struct type nonrec state = state type value = Cvalue.V.t type location = Precise_locs.precise_location + type origin = unit include (Traces: sig include Datatype.S_with_collections with type t = state @@ -856,68 +857,50 @@ module Internal = struct let log_category = Value_parameters.register_category "d-traces" - type origin = unit + let assign ki lv e _v _valuation state = + let trans = Assign (ki, lv.Eval.lval, lv.Eval.ltyp, e) in + `Value (Traces.add_trans state trans) + + let assume stmt e pos _valuation state = + let trans = Assume (stmt, e, pos) in + `Value (Traces.add_trans state trans) + + let start_call stmt call _valuation state = + let kf = call.Eval.kf in + if Kernel_function.is_definition kf then + let msg = Format.asprintf "start_call: %s (%b)" (Kernel_function.get_name call.Eval.kf) + (Kernel_function.is_definition call.Eval.kf) in + let state = Traces.add_trans state (Msg msg) in + let formals = List.map (fun arg -> arg.Eval.formal) call.Eval.arguments in + let state = Traces.add_trans state (EnterScope (kf, formals)) in + let state = List.fold_left (fun state arg -> + Traces.add_trans state + (Assign (Kstmt stmt, Cil.var arg.Eval.formal, + arg.Eval.formal.Cil_types.vtype, + arg.Eval.concrete))) state call.Eval.arguments in + `Value state + else + (** enter the scope of the dumb result variable *) + let var = call.Eval.return in + let state = match var with + | Some var -> Traces.add_trans state (EnterScope (kf, [var])) + | None -> state in + let exps = List.map (fun arg -> arg.Eval.concrete) call.Eval.arguments in + let state = Traces.add_trans state + (CallDeclared (call.Eval.kf, exps, Extlib.opt_map Cil.var var)) + in `Value {state with call_declared_function = true} + + let finalize_call _stmt call ~pre:_ ~post = + if post.call_declared_function + then `Value {post with call_declared_function = false} + else + let msg = Format.asprintf "finalize_call: %s" (Kernel_function.get_name call.Eval.kf) in + let state = Traces.add_trans post (Msg msg) in + `Value state - module Transfer (Valuation: Abstract_domain.Valuation - with type value = value - and type origin = origin - and type loc = Precise_locs.precise_location) - : Abstract_domain.Transfer - with type state = state - and type value = Cvalue.V.t - and type location = Precise_locs.precise_location - and type valuation = Valuation.t - = struct - type value = Cvalue.V.t - type state = t - type location = Precise_locs.precise_location - type valuation = Valuation.t - - let assign ki lv e _v _valuation state = - let trans = Assign (ki, lv.Eval.lval, lv.Eval.ltyp, e) in - `Value (Traces.add_trans state trans) - - let assume stmt e pos _valuation state = - let trans = Assume (stmt, e, pos) in - `Value (Traces.add_trans state trans) - - let start_call stmt call _valuation state = - let kf = call.Eval.kf in - if Kernel_function.is_definition kf then - let msg = Format.asprintf "start_call: %s (%b)" (Kernel_function.get_name call.Eval.kf) - (Kernel_function.is_definition call.Eval.kf) in - let state = Traces.add_trans state (Msg msg) in - let formals = List.map (fun arg -> arg.Eval.formal) call.Eval.arguments in - let state = Traces.add_trans state (EnterScope (kf, formals)) in - let state = List.fold_left (fun state arg -> - Traces.add_trans state - (Assign (Kstmt stmt, Cil.var arg.Eval.formal, - arg.Eval.formal.Cil_types.vtype, - arg.Eval.concrete))) state call.Eval.arguments in - `Value state - else - (** enter the scope of the dumb result variable *) - let var = call.Eval.return in - let state = match var with - | Some var -> Traces.add_trans state (EnterScope (kf, [var])) - | None -> state in - let exps = List.map (fun arg -> arg.Eval.concrete) call.Eval.arguments in - let state = Traces.add_trans state - (CallDeclared (call.Eval.kf, exps, Extlib.opt_map Cil.var var)) - in `Value {state with call_declared_function = true} - - let finalize_call _stmt call ~pre:_ ~post = - if post.call_declared_function - then `Value {post with call_declared_function = false} - else - let msg = Format.asprintf "finalize_call: %s" (Kernel_function.get_name call.Eval.kf) in - let state = Traces.add_trans post (Msg msg) in - `Value state - - let update _valuation state = `Value state - - let show_expr _valuation state fmt _expr = Traces.pretty fmt state - end + let update _valuation state = `Value state + + let show_expr _valuation state fmt _expr = Traces.pretty fmt state (* Memexec *) (* This domains infers no relation between variables. *) diff --git a/src/plugins/value/domains/unit_domain.ml b/src/plugins/value/domains/unit_domain.ml index bbc3d74c956..8fd5df76754 100644 --- a/src/plugins/value/domains/unit_domain.ml +++ b/src/plugins/value/domains/unit_domain.ml @@ -52,7 +52,6 @@ module Make include Static type value = Value.t type location = Loc.location - type origin = unit let eval_top = `Value (Value.top, ()), Alarmset.all @@ -61,18 +60,12 @@ module Make let backward_location _ _ _ loc value = `Value (loc, value) let reduce_further _ _ _ = [] - module Transfer - (_: Abstract_domain.Valuation with type value = value - and type loc = location) - = struct - - let update _ _ = `Value () - let assign _ _ _ _ _ _ = `Value () - let assume _ _ _ _ _ = `Value () - let start_call _ _ _ _ = `Value () - let finalize_call _ _ ~pre:_ ~post:_ = `Value () - let show_expr _ _ _ _ = () - end + let update _ _ = `Value () + let assign _ _ _ _ _ _ = `Value () + let assume _ _ _ _ _ = `Value () + let start_call _ _ _ _ = `Value () + let finalize_call _ _ ~pre:_ ~post:_ = `Value () + let show_expr _ _ _ _ = () let logic_assign _ _ ~pre:_ _ = () let evaluate_predicate _ _ _ = Alarmset.Unknown diff --git a/src/plugins/value/engine/evaluation.ml b/src/plugins/value/engine/evaluation.ml index c9ecc4766a4..63be1240664 100644 --- a/src/plugins/value/engine/evaluation.ml +++ b/src/plugins/value/engine/evaluation.ml @@ -195,6 +195,7 @@ module type S = sig module Valuation : Valuation with type value = value and type origin = origin and type loc = loc + val record: Valuation.t -> (value, loc, origin) Abstract_domain.valuation val evaluate : ?valuation:Valuation.t -> ?reduction:bool -> ?subdivnb:int -> state -> exp -> (Valuation.t * value) evaluated @@ -1483,6 +1484,11 @@ module Make module Valuation = Cache + let record valuation = + Abstract_domain.{ find = Valuation.find valuation; + fold = (fun f acc -> Valuation.fold f valuation acc); + find_loc = Valuation.find_loc valuation; } + let evaluate ?(valuation=Cache.empty) ?(reduction=true) ?subdivnb state expr = let eval, alarms = subdivided_forward_eval valuation ?subdivnb state expr in let result = diff --git a/src/plugins/value/engine/evaluation.mli b/src/plugins/value/engine/evaluation.mli index 258657a0c58..ad36f758101 100644 --- a/src/plugins/value/engine/evaluation.mli +++ b/src/plugins/value/engine/evaluation.mli @@ -39,6 +39,11 @@ module type S = sig and type origin = origin and type loc = loc + (** Evaluation functions store the results of an evaluation into [Valuation.t] + maps. Abstract domains read these results in [Abstract_domain.valuation] + records. This function converts the former into the latter. *) + val record: Valuation.t -> (value, loc, origin) Abstract_domain.valuation + (** [evaluate ~valuation state expr] evaluates the expression [expr] in the state [state], and returns the pair [result, alarms], where: - [alarms] are the set of alarms ensuring the soundness of the evaluation; diff --git a/src/plugins/value/engine/transfer_stmt.ml b/src/plugins/value/engine/transfer_stmt.ml index b0ffc5fa32a..3e821a34907 100644 --- a/src/plugins/value/engine/transfer_stmt.ml +++ b/src/plugins/value/engine/transfer_stmt.ml @@ -116,9 +116,6 @@ module Make (Abstract: Abstractions.Eva) = struct type value = Value.t type location = Location.location - (* Transfer functions. *) - module TF = Domain.Transfer (Eval.Valuation) - (* When using a product of domains, a product of states may have no concretization (if the domains have inferred incompatible properties) without being bottom (if the inter-reduction between domains are @@ -267,7 +264,8 @@ module Make (Abstract: Abstractions.Eva) = struct if is_ret then assert (Alarmset.is_empty alarms); Alarmset.emit kinstr alarms; eval >>- fun (assigned, valuation) -> - TF.assign kinstr {lval; ltyp; lloc} expr assigned valuation state + let valuation = Eval.record valuation in + Domain.assign kinstr {lval; ltyp; lloc} expr assigned valuation state let assign = assign_lv_or_ret ~is_ret:false let assign_ret = assign_lv_or_ret ~is_ret:true @@ -282,7 +280,7 @@ module Make (Abstract: Abstractions.Eva) = struct (* TODO: check not comparable. *) Alarmset.emit (Kstmt stmt) alarms; eval >>- fun valuation -> - TF.assume stmt expr positive valuation state + Domain.assume stmt expr positive (Eval.record valuation) state (* ------------------------------------------------------------------------ *) @@ -312,7 +310,7 @@ module Make (Abstract: Abstractions.Eva) = struct try let res = (* Process the call according to the domain decision. *) - match TF.start_call stmt call valuation state with + match Domain.start_call stmt call (Eval.record valuation) state with | `Value state -> Domain.Store.register_initial_state (Value_util.call_stack ()) state; !compute_call_ref stmt call state @@ -418,7 +416,7 @@ module Make (Abstract: Abstractions.Eva) = struct Eval.assume ~valuation state argument.concrete post_value in List.fold_left reduce_one_argument valuation reductions >>- fun valuation -> - TF.update valuation state + Domain.update (Eval.record valuation) state (* -------------------- Treat the results of a call ----------------------- *) @@ -474,7 +472,7 @@ module Make (Abstract: Abstractions.Eva) = struct let post = Domain.leave_scope kf_callee leaving_vars state in (* Computes the state after the call, from the post state at the end of the called function, and the pre state at the call site. *) - TF.finalize_call stmt call ~pre ~post >>- fun state -> + Domain.finalize_call stmt call ~pre ~post >>- fun state -> (* Backward propagates the [reductions] on the concrete arguments. *) reduce_arguments reductions state >>- fun state -> treat_return ~kf_callee lv call.return stmt state @@ -483,7 +481,7 @@ module Make (Abstract: Abstractions.Eva) = struct domains. Do not reduce them, and more importantly, do not remove them from the scope. (Because the instance from the initial, non-recursive, call are still present.) *) - TF.finalize_call stmt call ~pre ~post:state >>- fun state -> + Domain.finalize_call stmt call ~pre ~post:state >>- fun state -> treat_return ~kf_callee lv call.return stmt state in let states = @@ -590,12 +588,12 @@ module Make (Abstract: Abstractions.Eva) = struct (* Idem as for [print_state]. *) let show_expr = if Domain.log_category = Domain_product.product_category - then TF.show_expr + then Domain.show_expr else if Value_parameters.is_debug_key_enabled Domain.log_category then fun valuation state fmt exp -> Format.fprintf fmt "# %s: @[<hov>%a@]" - Domain.name (TF.show_expr valuation state) exp + Domain.name (Domain.show_expr valuation state) exp else fun _ _ _ _ -> () (* Frama_C_domain_show_each functions. *) @@ -603,8 +601,10 @@ module Make (Abstract: Abstractions.Eva) = struct let pretty fmt expr = let pp fmt = match fst (Eval.evaluate ~subdivnb state expr) with - | `Bottom -> Format.fprintf fmt "%s" (Unicode.bottom_string ()) - | `Value (valuation, _value) -> show_expr valuation state fmt expr + | `Bottom -> + Format.fprintf fmt "%s" (Unicode.bottom_string ()) + | `Value (valuation, _v) -> + show_expr (Eval.record valuation) state fmt expr in Format.fprintf fmt "%a : @[<h>%t@]" Printer.pp_exp expr pp in diff --git a/src/plugins/value/partitioning/partition.ml b/src/plugins/value/partitioning/partition.ml index 87940525f00..1d16932e3d9 100644 --- a/src/plugins/value/partitioning/partition.ml +++ b/src/plugins/value/partitioning/partition.ml @@ -226,9 +226,6 @@ struct (* --- Evaluation and split functions ------------------------------------- *) - (* Domains transfer functions. *) - module TF = Abstract.Dom.Transfer (Abstract.Eval.Valuation) - (* Evaluation with no reduction and no subdivision. *) let evaluate = Abstract.Eval.evaluate ~reduction:false ~subdivnb:0 @@ -278,7 +275,7 @@ struct let state = Abstract.Eval.assume ~valuation state exp value >>- fun valuation -> (* Check the reduction *) - TF.update valuation state + Abstract.Dom.update (Abstract.Eval.record valuation) state in match state with | `Value state -> diff --git a/src/plugins/value/register.ml b/src/plugins/value/register.ml index 0181dc86435..638dffd79ca 100644 --- a/src/plugins/value/register.ml +++ b/src/plugins/value/register.ml @@ -186,8 +186,6 @@ module Eva = (Main_locations.PLoc) (Cvalue_domain.State) -module Transfer = Cvalue_domain.State.Transfer (Eva.Valuation) - let inject_cvalue state = state, Locals_scoping.bottom () let bot_value = function @@ -199,7 +197,7 @@ let bot_state = function | `Value s -> s let update valuation state = - bot_state (Transfer.update valuation state >>-: fst) + bot_state (Cvalue_domain.State.update (Eva.record valuation) state >>-: fst) let rec eval_deps state e = match e.enode with -- GitLab