From 47820e55e2b2aa76a4e6925f69f0ef7c17f808d8 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Thu, 31 Mar 2022 01:56:20 +0200 Subject: [PATCH] [Eva] multidim: replace most exceptions by `Top handling --- .../abstract_interp/abstract_memory.ml | 101 +++++--- .../abstract_interp/abstract_offset.ml | 10 +- src/plugins/value/domains/multidim_domain.ml | 238 ++++++++++-------- 3 files changed, 195 insertions(+), 154 deletions(-) diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml index 74ef1091b7a..35f26eb7bdb 100644 --- a/src/kernel_services/abstract_interp/abstract_memory.ml +++ b/src/kernel_services/abstract_interp/abstract_memory.ml @@ -42,13 +42,15 @@ struct (* Applicative syntax *) let ( let+ ) = (>>-:) let ( and+ ) = zip -(* Monad syntax *) -let ( let* ) = (>>-) -let ( and* ) = zip + (* Monad syntax *) + let ( let* ) = (>>-) + let ( and* ) = zip end module Top = struct + [@@@warning "-32"] + let zip x y = match x, y with | `Top, _ | _, `Top -> `Top @@ -63,7 +65,12 @@ struct | `Value t -> `Value (f t) let (let+) = (>>-:) + let (and+) = zip let (let*) = (>>-) + let (and*) = zip + let of_option = function + | None -> `Top + | Some v -> `Value v end module TopBot = @@ -889,7 +896,7 @@ struct match Int_val.min_int (to_int_val ~oracle b) with | Some l -> `Value l | None -> - Kernel.warning ~current:true "cannot retrieve lower bound for %a" + Kernel.warning ~current:true "cannot retrieve a lower bound for %a" pretty b; `Top @@ -897,7 +904,7 @@ struct match Int_val.max_int (to_int_val ~oracle b) with | Some u -> `Value u | None -> - Kernel.warning ~current:true "cannot retrieve upper bound for %a" + Kernel.warning ~current:true "cannot retrieve an upper bound for %a" pretty b; `Top @@ -988,6 +995,7 @@ sig oracle:oracle -> Bound.Var.t -> Integer.t option -> t -> [ `Value of t | `Top ] val map : (submemory -> submemory) -> t -> t + val fold : (submemory -> 'a -> 'a) -> (bit -> 'b -> 'a) -> t -> 'b -> 'a val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t end @@ -1408,6 +1416,9 @@ struct let map f m = check { m with segments=List.map (fun (v,u) -> f v, u) m.segments } + let fold fs fp m acc = + List.fold_left (fun acc (v,_) -> fs v acc) (fp m.padding acc) m.segments + let add_segmentation_bounds ~oracle bounds m = let open TopBot in let add_bound m b = @@ -1756,14 +1767,26 @@ struct aux offset' u.union_value | Index (exp, index, elem_type, offset'), Array a when are_typ_compatible a.array_cell_type elem_type -> - let lindex, uindex = match Option.map Bound.of_exp exp with - | Some b -> b, b - | None | exception Bound.UnsupportedBoundExpression -> - let l, u = Int_val.min_and_max index in - Bound.of_integer (Option.get l), - Bound.of_integer (Option.get u) (* TODO: handle None *) + let open Top in + let m = + let+ lindex, uindex = match Option.map Bound.of_exp exp with + | Some b -> `Value (b, b) + | None | exception Bound.UnsupportedBoundExpression -> + let l, u = Int_val.min_and_max index in + let+ l = Top.of_option l + and+ u = Top.of_option u in + Bound.of_integer l, Bound.of_integer u + in + A.read ~oracle (aux offset') reduce a.array_value lindex uindex in - A.read ~oracle (aux offset') reduce a.array_value lindex uindex + begin match m with + | `Value v -> v + | `Top -> + A.fold + (fun m' acc -> reduce (aux offset' m') acc) + (fun p () -> aux offset' (Raw p)) + a.array_value () + end | _, m -> (* structure mismatch *) let r = Raw (raw m) in match offset with @@ -1798,33 +1821,35 @@ struct let+ union_value = aux ~weak offset' old.union_value in Union { old with union_value } | Index (exp, index, elem_type, offset') -> - let lindex, uindex, weak = match Option.map Bound.of_exp exp with - | Some b -> b, Bound.succ b, weak - | None | exception Bound.UnsupportedBoundExpression -> - let l, u = Int_val.min_and_max index in - let l = Option.get l and u = Option.get u in (* TODO: handle exceptions *) - Bound.of_integer l, Bound.(succ (of_integer u)), - weak || Integer.equal l u - in - match m with - | Array a when are_typ_compatible a.array_cell_type elem_type -> - let+ array_value = - match - A.write ~oracle (aux ~weak offset') a.array_value lindex uindex - with - | `Bottom -> `Bottom - | `Top -> - let b = raw m in - let+ new_value = aux ~weak offset' (Raw b) in - A.single b lindex uindex new_value - | `Value array_value -> `Value array_value + let m' = + let open TopBot in + let* lindex, uindex, weak = + match Option.map Bound.of_exp exp with + | Some b -> `Value (b, Bound.succ b, weak) + | None | exception Bound.UnsupportedBoundExpression -> + begin match Int_val.min_and_max index with + | None, _ | _, None -> `Top + | Some l, Some u -> + let l' = Bound.of_integer l + and u' = Bound.(succ (of_integer u)) in + `Value (l', u', weak || Integer.equal l u) + end in - Array { a with array_value } - | _ -> - let b = raw m in - let+ new_value = aux ~weak offset' (Raw b) in - let array_value = A.single b lindex uindex new_value in - Array { array_cell_type = elem_type ; array_value } + match m with + | Array a when are_typ_compatible a.array_cell_type elem_type -> + let+ array_value = + A.write ~oracle (aux ~weak offset') a.array_value lindex uindex in + Array { a with array_value } + | _ -> + let b = raw m in + let+ new_value = aux ~weak offset' (Raw b) in + let array_value = A.single b lindex uindex new_value in + Array { array_cell_type = elem_type ; array_value } + in + match m' with + | `Top -> (* No suitable bound for the partition *) + let+ v = f ~weak:true Cil.voidType m in v + | `Bottom | `Value _ as m -> m in aux let incr_bound ~oracle vi x m = (* TODO: keep subtree when nothing changes *) diff --git a/src/kernel_services/abstract_interp/abstract_offset.ml b/src/kernel_services/abstract_interp/abstract_offset.ml index 25eacbf4e4a..c7a77dc76b6 100644 --- a/src/kernel_services/abstract_interp/abstract_offset.ml +++ b/src/kernel_services/abstract_interp/abstract_offset.ml @@ -39,11 +39,11 @@ struct let (let+) = (>>-:) let (and+) = zip -let (let*) = (>>-) + let (let*) = (>>-) -let of_option = function - | None -> `Top - | Some x -> `Value x + let of_option = function + | None -> `Top + | Some x -> `Value x end type 'a or_top = [`Value of 'a | `Top] @@ -228,7 +228,7 @@ let rec of_int_val ~base_typ ~typ ival = let of_ival ~base_typ ~typ ival = match Ival.project_int_val ival with | Some ival -> of_int_val ~base_typ ~typ ival - | None -> `Top (* should not happen *) + | None -> `Top let index_of_term array_size t = (* Exact constant ranges *) match t.Cil_types.term_node with diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index bb66be5a321..2e81e5cf589 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -35,22 +35,28 @@ struct | `Value x, `Value y -> `Value (x,y) let (>>-) t f = match t with - | `Top -> `Top + | `Top -> `Top | `Value t -> f t let (>>-:) t f = match t with - | `Top -> `Top + | `Top -> `Top | `Value t -> `Value (f t) let (let+) = (>>-:) let (and+) = zip -let (let*) = (>>-) + let (let*) = (>>-) + let (and*) = zip -let of_option = function - | None -> `Top - | Some x -> `Value x + let of_option = function + | None -> `Top + | Some x -> `Value x end + +let pretty_bottom pp fmt = function + | `Bottom -> Format.fprintf fmt "%s" (Unicode.bottom_string ()) + | `Value value -> pp fmt value + let map_to_singleton map = let aux base offset = function | None -> Some (base, offset) @@ -81,6 +87,12 @@ struct try Some (Ival.project_int (V.project_ival (get_v cvalue))) with V.Not_based_on_null | Ival.Not_Singleton_Int -> None + let get_v_normalized x = + let v' = get_v x in + if V.is_bottom v' + then `Bottom + else `Value v' + let make i v = let initialized = match i with | Abstract_memory.SurelyInitialized -> true @@ -98,17 +110,17 @@ struct | Any (Top, i) -> make i (V.top_with_origin Origin.top) let to_bit v' = - let v = get_v v' - and initialization = - if is_initialized v' - then Abstract_memory.SurelyInitialized - else Abstract_memory.MaybeUninitialized - in - if V.is_bottom v - then Abstract_memory.Uninitialized - else if V.is_zero v - then Abstract_memory.Zero (initialization) - else Abstract_memory.Any (V.get_bases v, initialization) + match get_v_normalized v' with + | `Bottom -> Abstract_memory.Uninitialized + | `Value v -> + let initialization = + if is_initialized v' + then Abstract_memory.SurelyInitialized + else Abstract_memory.MaybeUninitialized + in + if V.is_zero v + then Abstract_memory.Zero (initialization) + else Abstract_memory.Any (V.get_bases v, initialization) let from_flagged fv = V_Or_Uninitialized.make @@ -122,7 +134,7 @@ end let no_oracle exp = match Cil.isInteger exp with - | None -> raise Abstract_interp.Error_Top + | None -> Value.top | Some i -> Value.inject_int i let convert_oracle oracle = @@ -186,7 +198,7 @@ struct let of_var (vi : Cil_types.varinfo) : t = Map.singleton (Base.of_varinfo vi) (`Value (Offset.of_var_address vi)) - let of_lval oracle ((host,offset) as lval : Cil_types.lval) : t = + let of_lval oracle ((host,offset) as lval : Cil_types.lval) : t or_top = let oracle' = convert_oracle oracle in let base_typ = Cil.typeOfLhost host in let offset = @@ -196,7 +208,7 @@ struct Offset.of_cil_offset oracle' base_typ offset in match host with | Var vi -> - Map.singleton (Base.of_varinfo vi) offset + `Value (Map.singleton (Base.of_varinfo vi) offset) | Mem exp -> let exp, index = match exp.enode with | BinOp (PlusPI, e1, e2, _typ) -> @@ -221,13 +233,16 @@ struct Map.add base offset' map in let loc = Locations.loc_bytes_to_loc_bits (oracle exp) in - Locations.Location_Bits.fold_i add loc Map.empty + try + `Value (Locations.Location_Bits.fold_topset_ok add loc Map.empty) + with Abstract_interp.Error_Top -> + `Top let of_term_lval env ((lhost, offset) as lval) = - let vi = match lhost with - | TVar ({lv_origin=Some vi}) -> vi - | TResult _ -> Option.get env.Abstract_domain.result - | _ -> raise Abstract_interp.Error_Top + let+ vi = match lhost with + | TVar ({lv_origin=Some vi}) -> `Value (vi) + | TResult _ -> Top.of_option env.Abstract_domain.result + | _ -> `Top in let base' = Base.of_varinfo vi in let offset' = Offset.of_term_offset vi.vtype offset in @@ -236,7 +251,7 @@ struct let of_term env t = match t.term_node with | TLval term_lval -> of_term_lval env term_lval - | _ -> raise Abstract_interp.Error_Top + | _ -> `Top let of_precise_loc loc = let loc' = Precise_locs.imprecise_location loc in @@ -434,9 +449,13 @@ struct let rec oracle exp = match exp.enode with | Lval lval -> - let loc = Location.of_lval oracle lval in - let value = get ~oracle state loc in - Value_or_Uninitialized.get_v (Bottom.non_bottom value) (* TODO: handle exception *) + begin match Location.of_lval oracle lval with + | `Top -> Value.top + | `Value loc -> + match get ~oracle state loc with + | `Bottom -> Value.bottom (* should never happen as location should not be empty *) + | `Value v -> Value_or_Uninitialized.get_v v + end | Const (CInt64 (i,_,_)) -> Value.inject_int i | Const (CReal (f,_,_)) -> Value.inject_float (Fval.singleton (Fval.F.of_float f)) | UnOp (op, e, typ) -> Value.forward_unop typ op (oracle e) @@ -591,41 +610,40 @@ struct let extract_lval ~oracle _context state lv _typ _loc = let oracle = fun exp -> match oracle exp with - | `Bottom, _ -> raise Abstract_interp.Error_Bottom - | `Value v, _ -> v + | `Value v, alarms when Alarmset.is_empty alarms -> v (* only use values safely evaluated *) + | _ -> Value.top in - try - let loc = Location.of_lval oracle lv in - let v = get ~oracle state loc in - (v >>-: fun v -> (Value_or_Uninitialized.get_v v, None)), - match v with - | `Bottom -> Alarmset.all + match Location.of_lval oracle lv with + | `Top -> (* can't evaluate location *) + `Value (Value.top, None), Alarmset.all + | `Value loc -> + match get ~oracle state loc with + | `Bottom -> `Bottom, Alarmset.all | `Value v -> + Value_or_Uninitialized.get_v_normalized v >>-: (fun v -> v, None), if Value_or_Uninitialized.is_initialized v then Alarmset.(set (Alarms.Uninitialized lv) True all) else Alarmset.all - with - | Abstract_interp.Error_Top -> `Value (Value.top, None), Alarmset.all - | Abstract_interp.Error_Bottom -> `Bottom, Alarmset.all (* Eva Transfer *) let make_oracle valuation : Cil_types.exp -> value = fun exp -> match valuation.Abstract_domain.find exp with - | `Top -> raise Abstract_interp.Error_Top - | `Value {value={v=`Bottom}} -> raise Abstract_interp.Error_Bottom + | `Top -> Value.top + | `Value {value={v=`Bottom}} -> Value.bottom | `Value {value={v=`Value value}} -> value let assume_exp valuation expr record state' = state' >>- fun state -> let oracle = make_oracle valuation in - try - match expr.enode with - | Lval lv -> - let value = Value_or_Uninitialized.from_flagged record.value in - if not (Value.is_topint (Value_or_Uninitialized.get_v value)) then - let loc = Location.of_lval oracle lv in + match expr.enode with + | Lval lv -> + let value = Value_or_Uninitialized.from_flagged record.value in + if not (Value.is_topint (Value_or_Uninitialized.get_v value)) then + match Location.of_lval oracle lv with + | `Top -> `Value state (* location evaluate to top, ignore *) + | `Value loc -> let update value' = let v = Value_or_Uninitialized.narrow value value' in if Value_or_Uninitialized.is_bottom v then `Bottom else `Value v @@ -633,11 +651,8 @@ struct if Location.is_singleton loc then reinforce ~oracle update state loc else `Value state - else `Value state - | _ -> `Value state - with - (* Failed to evaluate the location *) - Abstract_interp.Error_Top | Abstract_interp.Error_Bottom -> `Value state + else `Value state + | _ -> `Value state let assume_valuation valuation state = valuation.Abstract_domain.fold (assume_exp valuation) (`Value state) @@ -646,7 +661,6 @@ struct assume_valuation valuation state let update_array_segmentation_bounds vi expr (base_map,tracked as state) = - (* TODO: more general transfer function *) let incr = Option.bind expr (fun expr -> match expr.Cil_types.enode with | BinOp ((PlusA|PlusPI), { enode=Lval (Var vi', NoOffset) }, exp, _typ) @@ -697,20 +711,19 @@ struct update_array_segmentation_bounds vi expr state let assign_lval lval assigned_value oracle state = - try - let dst = Location.of_lval oracle lval in + match Location.of_lval oracle lval with + | `Top -> top + | `Value dst -> match assigned_value with | Assign value -> set ~oracle state dst (Value_or_Uninitialized.of_value value) - | Copy (right, _value) -> - try - let src = Location.of_lval oracle right.lval in + | Copy (right, value) -> + match Location.of_lval oracle right.lval with + | `Top -> + let b = Value_or_Uninitialized.(to_bit (from_flagged value)) in + erase ~oracle state dst b + | `Value src -> overwrite ~oracle state dst src - with Abstract_interp.Error_Top | Abstract_interp.Error_Bottom -> - erase ~oracle state dst Abstract_memory.Bit.top - with Abstract_interp.Error_Top | Abstract_interp.Error_Bottom -> - (* Failed to evaluate the left location *) - top let assign _kinstr left expr assigned_value valuation state = let state = update_array_segmentation left.lval (Some expr) state in @@ -743,15 +756,12 @@ struct let show_expr valuation state fmt expr = match expr.enode with | Lval lval | StartOf lval -> - begin try - let oracle = make_oracle valuation in - let loc = Location.of_lval oracle lval in - match extract ~oracle state loc with - | `Bottom -> Format.fprintf fmt "⊥" - | `Value value -> Memory.pretty fmt value - with Abstract_interp.Error_Top | Abstract_interp.Error_Bottom -> - (* can't evaluate location : print nothing *) - () + let oracle = make_oracle valuation in + begin match Location.of_lval oracle lval with + | `Top -> Format.fprintf fmt "%s" (Unicode.top_string ()) + | `Value loc -> + let m = extract ~oracle state loc in + pretty_bottom Memory.pretty fmt m end | _ -> () @@ -786,21 +796,20 @@ struct BaseMap.remove_loc base_map location, tracked let reduce_by_papp env li _labels args positive state = - try - match li.l_var_info.lv_name, args with - | "\\are_finite", [arg] -> - let loc,typ = Location.of_term env arg in - begin match Cil.unrollType (Logic_utils.logicCType typ) with - | TFloat (fkind,_) -> - let update = Value.backward_is_finite positive fkind - and oracle = mk_oracle state in - reinforce ~oracle (Value_or_Uninitialized.map' update) state loc - | _ | exception (Failure _) -> `Value state - end - | _ -> `Value state - with - | Abstract_interp.Error_Bottom -> `Bottom - | Abstract_interp.Error_Top -> `Value state + match li.l_var_info.lv_name, args with + | "\\are_finite", [arg] -> + begin match Location.of_term env arg with + | `Top -> `Value state (* can't resolve location, ignore *) + | `Value (loc,typ) -> + begin match Cil.unrollType (Logic_utils.logicCType typ) with + | TFloat (fkind,_) -> + let update = Value.backward_is_finite positive fkind + and oracle = mk_oracle state in + reinforce ~oracle (Value_or_Uninitialized.map' update) state loc + | _ | exception (Failure _) -> `Value state + end + end + | _ -> `Value state let reduce_by_predicate env state predicate truth = let rec reduce predicate truth state = @@ -821,19 +830,22 @@ struct (* Update the segmentation *) let lval = Cil_types.Var vi, offset in let oracle = mk_oracle state in - let loc = Location.of_lval oracle lval in - let update m offset = - let oracle = convert_oracle oracle in - Memory.segmentation_hint ~oracle m offset bounds - in - let state = write update state loc in - (* Update the references *) - let add acc e = - let r = Cil.extract_varinfos_from_exp e in - (Cil_datatype.Varinfo.Set.to_seq r |> List.of_seq) @ acc - in - let references = List.fold_left add [] bounds in - add_references_l state references (Location.bases loc) + begin match Location.of_lval oracle lval with + | `Top -> state + | `Value loc -> + let update m offset = + let oracle = convert_oracle oracle in + Memory.segmentation_hint ~oracle m offset bounds + in + let state = write update state loc in + (* Update the references *) + let add acc e = + let r = Cil.extract_varinfos_from_exp e in + (Cil_datatype.Varinfo.Set.to_seq r |> List.of_seq) @ acc + in + let references = List.fold_left add [] bounds in + add_references_l state references (Location.bases loc) + end | "eva_domain_scope" -> let domain,vars = Eva_annotations.read_domain_scope extension in if domain = "multidim" then @@ -851,19 +863,23 @@ struct let empty () = top let initialize_variable lval _loc ~initialized:_ init_value state = - let dst = Location.of_lval no_oracle lval in - let d = match init_value with - | Abstract_domain.Top -> Abstract_memory.Bit.numerical - | Abstract_domain.Zero -> Abstract_memory.Bit.zero - in - let oracle = mk_oracle state in (* Since dst has no offset, oracle is actually useless *) - erase ~oracle state dst d + match Location.of_lval no_oracle lval with + | `Top -> top + | `Value dst -> + let d = match init_value with + | Abstract_domain.Top -> Abstract_memory.Bit.numerical + | Abstract_domain.Zero -> Abstract_memory.Bit.zero + in + let oracle = mk_oracle state in (* Since dst has no offset, oracle is actually useless *) + erase ~oracle state dst d let initialize_variable_using_type _kind vi state = let lval = Cil.var vi in - let dst = Location.of_lval no_oracle lval in - let oracle = mk_oracle state in (* Since dst has no offset, oracle is actually useless *) - erase ~oracle state dst Abstract_memory.Bit.top + match Location.of_lval no_oracle lval with + | `Top -> top + | `Value dst -> + let oracle = mk_oracle state in (* Since dst has no offset, oracle is actually useless *) + erase ~oracle state dst Abstract_memory.Bit.top let relate _kf _bases _state = Base.SetLattice.empty -- GitLab