From 876dcf7c5e1e2be752a395b2e6c5f18ba2df8814 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Mon, 29 Nov 2021 17:43:12 +0100 Subject: [PATCH] [Eva] multidim: do not update segmentations that do not contain assigned variables --- .../abstract_interp/abstract_offset.ml | 18 ++++- .../abstract_interp/abstract_offset.mli | 1 + src/plugins/value/domains/multidim_domain.ml | 81 +++++++++++++++---- 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/kernel_services/abstract_interp/abstract_offset.ml b/src/kernel_services/abstract_interp/abstract_offset.ml index e315b281272..b4fe5713dd1 100644 --- a/src/kernel_services/abstract_interp/abstract_offset.ml +++ b/src/kernel_services/abstract_interp/abstract_offset.ml @@ -31,6 +31,7 @@ sig val of_ival : base_typ:Cil_types.typ -> typ:Cil_types.typ -> Ival.t -> t val of_term_offset : Cil_types.typ -> Cil_types.term_offset -> t val is_singleton : t -> bool + val references : t -> Cil_types.varinfo list end type typed_offset = @@ -87,7 +88,7 @@ struct | Some size_exp -> match Cil.constFoldToInt size_exp with | None -> None - | Some size when Integer.(gt size zero) -> Some (Integer.zero, size) + | Some size when Integer.(gt size zero) -> Some Integer.(zero, pred size) | Some _ -> None let array_range array_size = @@ -208,6 +209,17 @@ struct | Field (_fi, sub) -> is_singleton sub | Index (e, ival, _elem_typ, sub) -> (Option.is_some e || Int_val.is_singleton ival) && is_singleton sub + + let references = + let rec aux acc = function + | NoOffset _ -> acc + | Field (_, sub) | Index (None, _, _, sub) -> aux acc sub + | Index (Some e, _, _, sub) -> + let r = Cil.extract_varinfos_from_exp e in + let acc = (Cil_datatype.Varinfo.Set.to_seq r |> List.of_seq) @ acc in + aux acc sub + in + aux [] end module TypedOffsetOrTop = @@ -245,4 +257,8 @@ struct let is_singleton = function | `Top -> false | `Value o -> TypedOffset.is_singleton o + + let references = function + | `Top -> [] + | `Value o -> TypedOffset.references o end diff --git a/src/kernel_services/abstract_interp/abstract_offset.mli b/src/kernel_services/abstract_interp/abstract_offset.mli index 96723402c80..5b2dc622609 100644 --- a/src/kernel_services/abstract_interp/abstract_offset.mli +++ b/src/kernel_services/abstract_interp/abstract_offset.mli @@ -31,6 +31,7 @@ sig val of_ival : base_typ:Cil_types.typ -> typ:Cil_types.typ -> Ival.t -> t val of_term_offset : Cil_types.typ -> Cil_types.term_offset -> t val is_singleton : t -> bool + val references : t -> Cil_types.varinfo list (* variables referenced in the offset *) end type typed_offset = diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index deb69557b5e..f7ad735a528 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -108,12 +108,22 @@ struct let fold : (base-> offset -> 'a -> 'a) -> t -> 'a -> 'a = Map.fold + let bases map = + Map.fold (fun b _ acc -> b :: acc) map [] + let is_singleton map = match map_to_singleton map with | None -> false | Some (b,o) -> not (Base.is_weak b) && Offset.is_singleton o + let references map = + let module Set = Cil_datatype.Varinfo.Set in + let add_refs _b o = + Set.union (Set.of_list (Offset.references o)) + in + Map.fold add_refs map Set.empty |> Set.to_seq |> List.of_seq + let of_var (vi : Cil_types.varinfo) : t = Map.singleton (Base.of_varinfo vi) (`Value (NoOffset vi.vtype)) @@ -184,7 +194,7 @@ struct include Datatype.Make (Prototype) let pretty = Memory.pretty - let pretty_debug = Memory.pretty + let _pretty_debug = Memory.pretty let top = Memory.top let is_top = Memory.is_top let is_included = Memory.is_included @@ -233,15 +243,30 @@ struct Memory.overwrite ~oracle ~weak dst loc src end +(* References to variables inside array segmentation. + For instance if an array A is described with the segmentation + 0..i-1 ; i ; i+1..10 + then, everytime i is changed, the segmentation must be updated. This requires + referencing every base where at least one segmentation references i. *) +module References = +struct + include Base.Hptset (* The set of bases referencing the variable *) +end module DomainLattice = struct (* The domain is essentially a map from bases to individual memory abstractions *) module Initial_Values = struct let v = [[]] end module Deps = struct let l = [Ast.self] end + module V = + struct + include Datatype.Pair (Memory) (References) + let pretty_debug = pretty + let top = Memory.top, References.empty + end include Hptmap.Make - (Base.Base) (Memory) + (Base.Base) (V) (Hptmap.Comp_unused) (Initial_Values) (Deps) type state = t @@ -268,7 +293,7 @@ struct | CLogic_Var _ | String _ -> false let find_or_top (state : state) (b : base) = - try find b state with Not_found -> Memory.top + try find b state with Not_found -> V.top let remove_var (state : state) (v : Cil_types.varinfo) = remove (Base.of_varinfo v) state @@ -288,7 +313,7 @@ struct state -> mdlocation -> a or_bottom = fun map reduce state loc -> let f base off acc = - let v = map (find_or_top state base) off in + let v = map (fst (find_or_top state base)) off in Bottom.join reduce (`Value v) acc in Location.fold f loc `Bottom @@ -317,13 +342,20 @@ struct let write (update : memory -> offset -> memory) (state : state) (loc : mdlocation) : state = + let bases = Location.bases loc in let f base off state = if covers_base base then - add base (update (find_or_top state base) off) state + let memory, refs = find_or_top state base in + add base (update memory off, refs) state else state + and add_ref state vi = + let base = Base.of_varinfo vi in + let memory, refs = find_or_top state base in + add base (memory, References.union refs (References.of_list bases)) state in - Location.fold f loc state + let state = Location.fold f loc state in + List.fold_left add_ref state (Location.references loc) let set (state : state) (dst : mdlocation) (v : value) : state = let weak = not (Location.is_singleton dst) @@ -356,7 +388,7 @@ struct let cache = cache_name "is_included" in let decide_fst _b _v1 = true (* v2 is top *) in let decide_snd _b _v2 = false (* v1 is top, v2 is not *) in - let decide_both _ v1 v2 = Memory.is_included v1 v2 in + let decide_both _ (m1,_r1) (m2,_r2) = Memory.is_included m1 m2 in let decide_fast s t = if s == t then PTrue else PUnknown in binary_predicate cache UniversalPredicate ~decide_fast ~decide_fst ~decide_snd ~decide_both @@ -375,9 +407,10 @@ struct | Right -> mk_oracle s2 in let cache = Hptmap_sig.NoCache - and decide _ v1 v2 = - let r = Memory.join ~oracle v1 v2 in - if Memory.(is_top r) then None else Some r + and decide _ (m1,r1) (m2,r2) = + let m = Memory.join ~oracle m1 m2 + and r = References.union r1 r2 in + if Memory.(is_top m) then None else Some (m,r) in inter ~cache ~symmetric:false ~idempotent:true ~decide s1 s2 @@ -387,9 +420,10 @@ struct | Right -> mk_oracle s2 and _,get_hints = Widen.getWidenHints kf stmt in let cache = Hptmap_sig.NoCache - and decide base b1 b2 = - let r = Memory.widen ~oracle (get_hints base) b1 b2 in - if Memory.(is_top r) then None else Some r + and decide base (m1,r1) (m2,r2) = + let m = Memory.widen ~oracle (get_hints base) m1 m2 + and r = References.union r1 r2 in + if Memory.(is_top m) then None else Some (m,r) in inter ~cache ~symmetric:false ~idempotent:true ~decide s1 s2 end @@ -456,8 +490,6 @@ struct let update_array_segmentation_bounds vi expr state = (* TODO: more general transfer function *) - (* TODO: only update memory models where the lval is present in the - segmentation *) let incr = Option.bind expr (fun expr -> match expr.Cil_types.enode with | BinOp ((PlusA|PlusPI), { enode=Lval (Var vi', NoOffset) }, exp, _typ) @@ -474,7 +506,24 @@ struct (* Very important : oracle must be the oracle before a non-invertible assignement of the bound to allow removing of eventual empty slice before the bound leaves the segmentation. *) - map (Memory.incr_bound ~oracle:(mk_oracle state) vi incr) state + let oracle = mk_oracle state in + let references = snd (find_or_top state (Base.of_varinfo vi)) in + let update_ref base state = + let update (memory, refs) = + Memory.incr_bound ~oracle vi incr memory, refs + in + replace (Option.map update) base state + in + let state = References.fold update_ref references state in + (* If increment is None, every reference to vi should have been removed by + Memory.incr_bound *) + if Option.is_none incr then + replace + (Option.map (fun (memory, _refs) -> memory, References.empty)) + (Base.of_varinfo vi) + state + else + state let update_array_segmentation lval expr state = match lval with -- GitLab