From 9c61e985665a2829d84868f8ac6f94ecccacb82e Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Sun, 26 Dec 2021 16:10:43 +0100 Subject: [PATCH] [Eva] multidim: Prove initialization alarms --- .../abstract_interp/abstract_memory.ml | 118 ++++++++++---- .../abstract_interp/abstract_memory.mli | 14 +- src/plugins/value/domains/multidim_domain.ml | 154 ++++++++++++------ tests/value/oracle/multidim.res.oracle | 8 +- 4 files changed, 204 insertions(+), 90 deletions(-) diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml index d2e361038cc..16b4b49c667 100644 --- a/src/kernel_services/abstract_interp/abstract_memory.ml +++ b/src/kernel_services/abstract_interp/abstract_memory.ml @@ -84,10 +84,51 @@ let are_typ_compatible t1 t2 = (* --- Imprecise bits abstraction --- *) (* ------------------------------------------------------------------------ *) +type initialization = + | SurelyInitialized + | MaybeUninitialized + type bit = | Uninitialized - | Zero - | Any of Base.SetLattice.t + | Zero of initialization + | Any of Base.SetLattice.t * initialization + +module Initialization = +struct + let pretty fmt = function + | SurelyInitialized -> () + | MaybeUninitialized -> + Format.fprintf fmt "or UNINITIALIZED" + + let hash = function + | SurelyInitialized -> 3 + | MaybeUninitialized -> 7 + + let equal i1 i2 = + match i1, i2 with + | SurelyInitialized, SurelyInitialized + | MaybeUninitialized, MaybeUninitialized -> true + | _, _ -> false + + let compare i1 i2 = + match i1, i2 with + | SurelyInitialized, SurelyInitialized + | MaybeUninitialized, MaybeUninitialized -> 0 + | SurelyInitialized, MaybeUninitialized -> -1 + | MaybeUninitialized, SurelyInitialized -> 1 + + let is_included i1 i2 = + match i1, i2 with + | SurelyInitialized, _ + | _, MaybeUninitialized -> true + | MaybeUninitialized, SurelyInitialized -> false + + let join i1 i2 = + match i1, i2 with + | SurelyInitialized, SurelyInitialized -> SurelyInitialized + | MaybeUninitialized, _ + | _, MaybeUninitialized -> MaybeUninitialized +end module Bit = struct @@ -96,54 +137,72 @@ struct type t = bit let uninitialized = Uninitialized - let zero = Zero - let numerical = Any Bases.empty - let top = Any Bases.top + let zero = Zero SurelyInitialized + let numerical = Any (Bases.empty, SurelyInitialized) + let top = Any (Bases.top, MaybeUninitialized) + let pretty fmt = function - | Uninitialized -> Format.fprintf fmt "UNINITIALIZED" - | Zero -> Format.fprintf fmt "0" - | Any (Set set) when Base.SetLattice.O.is_empty set -> - Format.fprintf fmt "[--..--]" + | Uninitialized -> + Format.fprintf fmt "UNINITIALIZED" + | Zero i -> + Format.fprintf fmt "0%a" Initialization.pretty i + | Any (Set set, i) when Base.SetLattice.O.is_empty set -> + Format.fprintf fmt "[--..--]%a" Initialization.pretty i | Any _ -> Format.fprintf fmt "T" let is_any = function Any _ -> true | _ -> false let hash = function | Uninitialized -> 7 - | Zero -> 3 - | Any set -> Bases.hash set + | Zero i -> Hashtbl.hash (3, Initialization.hash i) + | Any (set, i)-> Hashtbl.hash (53, Bases.hash set, Initialization.hash i) let equal d1 d2 = match d1,d2 with | Uninitialized, Uninitialized -> true - | Zero, Zero -> true - | Any set1, Any set2 -> Bases.equal set1 set2 + | Zero i1, Zero i2 -> Initialization.equal i1 i2 + | Any (set1,i1), Any (set2,i2) -> + Bases.equal set1 set2 && Initialization.equal i1 i2 | _, _ -> false let compare d1 d2 = match d1,d2 with | Uninitialized, Uninitialized -> 0 - | Zero, Zero -> 0 - | Any set1, Any set2 -> Bases.compare set1 set2 + | Zero i1, Zero i2 -> Initialization.compare i1 i2 + | Any (set1,i1), Any (set2,i2) -> + Bases.compare set1 set2 <?> (Initialization.compare, i1, i2) | Uninitialized, _ -> 1 | _, Uninitialized -> -1 - | Zero, _ -> 1 - | _, Zero -> -1 + | Zero _, _ -> 1 + | _, Zero _-> -1 + + let initialization = function + | Uninitialized -> MaybeUninitialized + | Zero i -> i + | Any (_,i) -> i let is_included d1 d2 = + Initialization.is_included (initialization d1) (initialization d2) && match d1, d2 with | Uninitialized, _ -> true | _, Uninitialized -> false - | Zero, _ -> true - | _, Zero -> false - | Any set1, Any set2 -> Bases.is_included set1 set2 + | Zero _, _ -> true + | _, Zero _ -> false + | Any (set1,_), Any (set2,_) -> Bases.is_included set1 set2 let join d1 d2 = match d1, d2 with - | Uninitialized, d | d, Uninitialized -> d - | Zero, d | d, Zero -> d - | Any set1, Any set2 -> Any (Bases.join set1 set2) + | Uninitialized, Uninitialized -> Uninitialized + | Zero i1, Zero i2 -> + Zero (Initialization.join i1 i2) + | Any (set1,i1), Any (set2,i2) -> + Any (Bases.join set1 set2, Initialization.join i1 i2) + | Zero _, Uninitialized + | Uninitialized, Zero _ -> Zero MaybeUninitialized + | Any (set,i), (Zero _ | Uninitialized as b) + | (Zero _ | Uninitialized as b), Any (set,i) -> + Any (set, Initialization.join i (initialization b)) end @@ -259,7 +318,7 @@ struct include Datatype.Serializable_undefined include M let name = "Abstract_Memory.Structures.Values" - let reprs = [ of_raw Zero ] + let reprs = [ of_raw Bit.zero ] end) let pretty_debug = pretty end @@ -416,7 +475,7 @@ struct include Datatype.Serializable_undefined include S let name = "Abstract_Memory.Disjunction.S" - let reprs = [ of_raw Zero ] + let reprs = [ of_raw Bit.zero ] let eval_key key m = M.to_singleton_int (read m key) let suitable_key key m = Option.is_some (eval_key key m) let keys_candidates ci m = @@ -1470,7 +1529,7 @@ struct let rec to_singleton_int = function | Scalar s -> V.to_integer s.scalar_value - | Raw Zero -> Some Integer.zero + | Raw (Zero _) -> Some Integer.zero | Union u -> to_singleton_int u.union_value | _ -> None @@ -1688,7 +1747,7 @@ struct Array { array_cell_type = elem_type ; array_value } in aux - let incr_bound ~oracle vi x = (* TODO: keep subtree when nothing changes *) + let incr_bound ~oracle vi x m = (* TODO: keep subtree when nothing changes *) let rec aux = function | (Raw _ | Scalar _) as m -> m | Struct s -> Struct { s with struct_value = S.map aux s.struct_value } @@ -1697,7 +1756,10 @@ struct | Array a -> let array_value = A.incr_bound ~oracle vi x a.array_value in Array { a with array_value=A.map aux array_value } - in aux + in + let r = aux m in + (* debug true "bounds updated@.%a@.to%a" pretty m pretty r; *) + r let add_segmentation_bounds ~oracle ~typ bounds = let convert_bound exp = diff --git a/src/kernel_services/abstract_interp/abstract_memory.mli b/src/kernel_services/abstract_interp/abstract_memory.mli index 41f304a05f0..7a9ed21b0d5 100644 --- a/src/kernel_services/abstract_interp/abstract_memory.mli +++ b/src/kernel_services/abstract_interp/abstract_memory.mli @@ -22,12 +22,18 @@ type size = Integer.t +type initialization = + | SurelyInitialized + | MaybeUninitialized + type bit = | Uninitialized (* Uninitialized everywhere *) - | Zero (* Zero or uninitialized everywhere *) - | Any of Base.SetLattice.t (* Undetermined anywhere, and can contain bits - of pointers. If the base set is empty, - the bit can only come from numerical values. *) + | Zero of initialization (* Zero or uninitialized everywhere *) + | Any of Base.SetLattice.t * initialization + (* Undetermined anywhere, and can contain bits + of pointers. If the base set is empty, + the bit can only come from numerical values. *) + module Bit : sig diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index 112b20da720..6f9210091e0 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -37,25 +37,6 @@ struct include Cvalue.V include Cvalue_forward (* for fallback oracles *) - let to_integer cvalue = - try Some (Ival.project_int (project_ival cvalue)) - with Not_based_on_null | Ival.Not_Singleton_Int -> None - - let of_integer = inject_int - - let of_bit = function - | Abstract_memory.Uninitialized -> bottom - | Zero -> inject_int Integer.zero - | Any (Set s) -> inject_top_origin Origin.top s - | Any (Top) -> top_with_origin Origin.top - - let to_bit v = - if is_bottom v - then Abstract_memory.Uninitialized - else if is_zero v - then Abstract_memory.Zero - else Abstract_memory.Any (get_bases v) - let backward_is_finite positive fkind v = let prec = Fval.kind fkind in try @@ -65,10 +46,58 @@ struct `Value v end +module Value_or_Uninitialized = +struct + open Cvalue + include V_Or_Uninitialized + + let to_integer cvalue = + try Some (Ival.project_int (V.project_ival (get_v cvalue))) + with V.Not_based_on_null | Ival.Not_Singleton_Int -> None + + let make i v = + let initialized = match i with + | Abstract_memory.SurelyInitialized -> true + | Abstract_memory.MaybeUninitialized -> false + in + V_Or_Uninitialized.make ~initialized ~escaping:false v + + let of_value v = + V_Or_Uninitialized.make ~initialized:true ~escaping:false v + + let of_bit = function + | Abstract_memory.Uninitialized -> uninitialized + | Zero i -> make i (V.inject_int Integer.zero) + | Any (Set s,i) -> make i (V.inject_top_origin Origin.top s) + | 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) + + let from_flagged fv = + V_Or_Uninitialized.make + ~initialized:fv.initialized + ~escaping:false + (Bottom.value ~bottom:V.bottom fv.v) + + let map' f v = + f (get_v v) >>-: fun new_v -> map (fun _ -> new_v) v +end + let no_oracle exp = match Cil.isInteger exp with | None -> raise Abstract_interp.Error_Top - | Some i -> Value.of_integer i + | Some i -> Value.inject_int i let convert_oracle oracle = fun exp -> @@ -200,7 +229,7 @@ struct let disjunctive_invariants = Parameters.MultidimDisjunctiveInvariants.get end - module Memory = Abstract_memory.TypedMemory (Config) (Value) + module Memory = Abstract_memory.TypedMemory (Config) (Value_or_Uninitialized) module Prototype = (* Datatype *) @@ -221,12 +250,14 @@ struct let narrow = fun m1 _m2 -> m1 let join = Memory.join let smash ~oracle = Memory.join ~oracle:(fun _ -> oracle) - let widen h = Memory.widen (fun ~size v1 v2 -> Value.widen (size,h) v1 v2) + let widen h = + Memory.widen + (fun ~size v1 v2 -> Value_or_Uninitialized.widen (size,h) v1 v2) let incr_bound = Memory.incr_bound let get ~oracle m loc = match loc with - | `Top -> Value.top + | `Top -> Value_or_Uninitialized.top | `Value loc -> Memory.get ~oracle m loc let extract ~oracle m loc = @@ -292,6 +323,7 @@ struct type state = t type value = Value.t + type value_or_uninitialized = Value_or_Uninitialized.t type base = Base.t type offset = Location.offset type memory = Memory.t @@ -339,18 +371,18 @@ struct in Location.fold f loc `Bottom - and get (state : state) (src : mdlocation) : value or_bottom = + and get (state : state) (src : mdlocation) : value_or_uninitialized or_bottom = let oracle = mk_oracle state in - read (Memory.get ~oracle) Value.join state src + read (Memory.get ~oracle) Value_or_Uninitialized.join state src - and mk_oracle' (state : state) : Cil_types.exp -> Value.t = + and mk_oracle' (state : state) : Cil_types.exp -> value = (* Until Eva gives access to good oracles, we use this poor stupid oracle instead *) let rec oracle exp = match exp.enode with | Lval lval -> let value = get state (Location.of_lval oracle lval) in - Bottom.non_bottom value (* TODO: handle exception *) + Value_or_Uninitialized.get_v (Bottom.non_bottom value) (* TODO: handle exception *) | Const (CInt64 (i,_,_)) -> Value.inject_int i | UnOp (op, e, typ) -> Value.forward_unop typ op (oracle e) | BinOp (op, e1, e2, TFloat (fkind, _)) -> @@ -369,13 +401,21 @@ struct in fun exp -> oracle (Cil.constFold true exp) - and mk_oracle (state : state) : Abstract_memory.oracle = + and convert_oracle (oracle : Cil_types.exp -> value) + : Abstract_memory.oracle = fun exp -> try - Value.project_ival (mk_oracle' state exp) + Value.project_ival (oracle exp) with Value.Not_based_on_null -> Ival.top (* TODO: should it just not happen ? *) + and convert_oracle_or_default state = function (* TODO: use everywhere *) + | Some oracle -> convert_oracle oracle + | None -> mk_oracle state + + and mk_oracle (state : state) : Abstract_memory.oracle = + convert_oracle (mk_oracle' state) + let extract (state : state) (src : mdlocation) : Memory.t or_bottom = let oracle = mk_oracle state in read (Memory.extract ~oracle) (Memory.smash ~oracle) state src @@ -407,7 +447,7 @@ struct (* Result can never be bottom if update never returns bottom *) Bottom.non_bottom (write' (fun m o -> `Value (update m o)) state loc) - let set (state : state) (dst : mdlocation) (v : value) : state = + let set (state : state) (dst : mdlocation) (v : value_or_uninitialized) : state = let weak = not (Location.is_singleton dst) and oracle = mk_oracle state in write (Memory.set ~oracle ~weak v) state dst @@ -425,9 +465,11 @@ struct and oracle = mk_oracle state in write (fun m off -> Memory.erase ~oracle ~weak m off b) state dst - let reinforce (f : value -> value or_bottom) + let reinforce + ?oracle + (f : value_or_uninitialized -> value_or_uninitialized or_bottom) (state : state) (dst : mdlocation) : state or_bottom = - let oracle = mk_oracle state in + let oracle = convert_oracle_or_default state oracle in write' (fun m off -> Memory.reinforce ~oracle f m off) state dst (* Lattice *) @@ -495,15 +537,19 @@ struct | `Bottom, _ -> raise Abstract_interp.Error_Bottom | `Value v, _ -> v in - let v = - try - let loc = Location.of_lval oracle lv in - get state loc >>-: fun v -> v, None - with - | Abstract_interp.Error_Top -> `Value (Value.top, None) - | Abstract_interp.Error_Bottom -> `Bottom - in - v, Alarmset.all + try + let loc = Location.of_lval oracle lv in + let v = get state loc in + (v >>-: fun v -> (Value_or_Uninitialized.get_v v, None)), + match v with + | `Bottom -> Alarmset.all + | `Value v -> + 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 *) @@ -518,17 +564,19 @@ struct state' >>- fun state -> let oracle = make_oracle valuation in try - match expr.enode, record.value.v with - | Lval lv, `Value value when not (Value.is_topint value) -> - let loc = Location.of_lval oracle lv in - let update value' = - let v = Value.narrow value value' in - if Value.is_bottom v then `Bottom else `Value v - in - if Location.is_singleton loc - then reinforce update state loc + 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 + let update value' = + let v = Value_or_Uninitialized.narrow value value' in + if Value_or_Uninitialized.is_bottom v then `Bottom else `Value v + in + if Location.is_singleton loc + then reinforce ~oracle update state loc + else `Value state else `Value state - | _, `Bottom -> `Value state (* Indeterminate value, ignore *) | _ -> `Value state with (* Failed to evaluate the location *) @@ -592,7 +640,7 @@ struct let dst = Location.of_lval oracle lval in match assigned_value with | Assign value -> - set state dst value + set state dst (Value_or_Uninitialized.of_value value) | Copy (right, _value) -> try let src = Location.of_lval oracle right.lval in @@ -681,7 +729,7 @@ struct begin match Cil.unrollType (Logic_utils.logicCType typ) with | TFloat (fkind,_) -> let update = Value.backward_is_finite positive fkind in - reinforce update state loc + reinforce (Value_or_Uninitialized.map' update) state loc | _ | exception (Failure _) -> `Value state end | _ -> `Value state diff --git a/tests/value/oracle/multidim.res.oracle b/tests/value/oracle/multidim.res.oracle index 9d11b591920..634b46a94bf 100644 --- a/tests/value/oracle/multidim.res.oracle +++ b/tests/value/oracle/multidim.res.oracle @@ -42,7 +42,7 @@ } y2 : # cvalue: .t1[0].f ∈ {4.} or UNINITIALIZED {.t1{[0].i; [1..3]}; .t2[0..3]} ∈ UNINITIALIZED - # multidim: { .t1[0] = { .f = {4.} } } + # multidim: { .t1[0] = { .f = {4.} or UNINITIALIZED } } z : # cvalue: {0} # multidim: 0 [eva] multidim.c:45: @@ -64,8 +64,8 @@ .t2[3].f ∈ [-3.40282346639e+38 .. 3.40282346639e+38] .t2[3].i ∈ [--..--] # multidim: { - .t1[0 .. 3] = { .f = {{ ANYTHING }} }, - .t2[0 .. 3] = { .f = {{ ANYTHING }} } + .t1[0 .. 3] = { .f = {{ ANYTHING }} or UNINITIALIZED }, + .t2[0 .. 3] = { .f = {{ ANYTHING }} or UNINITIALIZED } } [eva] computing for function f <- main1 <- main. Called from multidim.c:47. @@ -207,8 +207,6 @@ [eva] multidim.c:147: function Frama_C_interval: precondition 'order' got status valid. [eva] Done for function Frama_C_interval -[eva:alarm] multidim.c:148: Warning: - accessing uninitialized left-value. assert \initialized(&t[j].typ); [eva] multidim.c:150: Frama_C_show_each_INT: {42} [eva] multidim.c:153: Frama_C_show_each_FLOAT: {42.} [eva] Recording results for main7 -- GitLab