(**************************************************************************) (* *) (* This file is part of WP plug-in of Frama-C. *) (* *) (* Copyright (C) 2007-2020 *) (* CEA (Commissariat a l'energie atomique et aux energies *) (* alternatives) *) (* *) (* you can redistribute it and/or modify it under the terms of the GNU *) (* Lesser General Public License as published by the Free Software *) (* Foundation, version 2.1. *) (* *) (* It is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) (* GNU Lesser General Public License for more details. *) (* *) (* See the GNU Lesser General Public License version 2.1 *) (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) (* -------------------------------------------------------------------------- *) (* --- Empty Memory Model --- *) (* -------------------------------------------------------------------------- *) open Cil_types open Cil_datatype open Ctypes open Lang open Lang.F open Sigs open Definitions open MemMemory let dkey_layout = Wp_parameters.register_category "layout" module L = Qed.Logic (* -------------------------------------------------------------------------- *) (* --- Model Configuration --- *) (* -------------------------------------------------------------------------- *) let datatype = "MemTyped" let hypotheses p = p let configure () = begin let orig_pointer = Context.push Lang.pointer t_addr in let orig_null = Context.push Cvalues.null (p_equal a_null) in let rollback () = Context.pop Lang.pointer orig_pointer ; Context.pop Cvalues.null orig_null ; in rollback end let configure_ia = let no_binder = { bind = fun _ f v -> f v } in fun _vertex -> no_binder (* -------------------------------------------------------------------------- *) (* --- Model Parameters --- *) (* -------------------------------------------------------------------------- *) type pointer = NoCast | Fits | Unsafe let pointer = Context.create "MemTyped.pointer" (* -------------------------------------------------------------------------- *) (* --- Model Semantics --- *) (* -------------------------------------------------------------------------- *) (* the semantic is defined using these notions: - base, offset: has the usual C semantic - memory model consists of: - an allocation table A : base -> int - for each kind of cell (char,int,float,pointer(ptr)) with type T, a map (M_T) : addr -> T - a pointer is record { base ; offset }, offset are in number of cells - allocation table: indicate the size (in number of cell not sizeof) allocated of each base. - = 0 : free - > 0 : allocated read-write - < 0 : allocated read only semantic of all these functions: - region(base -> int): the regions represent the natural partition of the memory by the time when it have been allocated. So the regions are identified by a number. So the addresses in one base are all in the same region. Caveat the [region] function doesn't associate the base to its region directly but to a congruence class that depend of each function but which keeps the order: - = 1 : regions corresponds to formals - = 2 : regions corresponds to locals - > 2 : freshly allocated bases (malloc) - = 0 : globals (except string literals) - < 0 : string literals (-its id) - framed(M_ptr): All pointer values accessible from the memory M (of pointers), lives in region <= 0. Hence separated from locals, formals, and freshly allocated in the current function. - linked(A): The proposition [linked] indicate that an allocation table were the globals are allocated. - sconst(M_char): Indicate that the memory M (of chars) contains the values of string literals at their bases. *) (* -------------------------------------------------------------------------- *) (* --- Chunks --- *) (* -------------------------------------------------------------------------- *) type chunk = | M_int of Ctypes.c_int | M_char | M_f32 | M_f64 | M_pointer | T_alloc | T_init module Chunk = struct type t = chunk let self = "typed" let int_rank = function | CBool -> 0 | UInt8 -> 1 | SInt8 -> 2 | UInt16 -> 3 | SInt16 -> 4 | UInt32 -> 5 | SInt32 -> 6 | UInt64 -> 7 | SInt64 -> 8 let rank = function | M_char -> -1 | M_int i -> int_rank i | M_f32 -> 9 | M_f64 -> 10 | M_pointer -> 11 | T_alloc -> 12 | T_init -> 13 let hash = rank let name = function | M_int _ -> "Mint" | M_char -> "Mchar" | M_f32 -> "Mf32" | M_f64 -> "Mf64" | M_pointer -> "Mptr" | T_alloc -> "Malloc" | T_init -> "Init" let compare a b = rank a - rank b let equal = (=) let pretty fmt c = Format.pp_print_string fmt (name c) let detailed_pretty fmt = function | M_int i -> Format.fprintf fmt "M%a" Ctypes.pp_int i | m -> Format.pp_print_string fmt (name m) let val_of_chunk = function | M_int _ | M_char -> L.Int | M_f32 -> Cfloat.tau_of_float Ctypes.Float32 | M_f64 -> Cfloat.tau_of_float Ctypes.Float64 | M_pointer -> t_addr | T_alloc -> L.Int | T_init -> L.Bool let tau_of_chunk = function | M_int _ | M_char -> L.Array(t_addr,L.Int) | M_pointer -> L.Array(t_addr,t_addr) | M_f32 -> L.Array(t_addr,Cfloat.tau_of_float Ctypes.Float32) | M_f64 -> L.Array(t_addr,Cfloat.tau_of_float Ctypes.Float64) | T_alloc -> t_malloc | T_init -> t_init let basename_of_chunk = name let is_framed _ = false end module Heap = Qed.Collection.Make(Chunk) module Sigma = Sigma.Make(Chunk)(Heap) type loc = term (* of type addr *) (* -------------------------------------------------------------------------- *) (* --- Utilities on locations --- *) (* -------------------------------------------------------------------------- *) let m_int i = if Ctypes.is_char i then M_char else M_int i let m_float = function Float32 -> M_f32 | Float64 -> M_f64 let rec footprint = function | C_int i -> Heap.Set.singleton (m_int i) | C_float f -> Heap.Set.singleton (m_float f) | C_pointer _ -> Heap.Set.singleton M_pointer | C_array a -> footprint (object_of a.arr_element) | C_comp c -> footprint_comp c and footprint_comp c = List.fold_left (fun ft f -> Heap.Set.union ft (footprint (object_of f.ftype)) ) Heap.Set.empty c.cfields let init_footprint _ _ = Heap.Set.singleton T_init let value_footprint obj _l = footprint obj let rec length_of_object = function | C_int _ | C_float _ | C_pointer _ -> 1 | C_comp c -> length_of_comp c | C_array { arr_flat = Some { arr_size = n } ; arr_element = elt } -> n * (length_of_typ elt) | C_array _ as a -> if Wp_parameters.ExternArrays.get () then max_int else Warning.error ~source:"Typed Model" "Undefined array-size (%a)" Ctypes.pretty a and length_of_typ t = length_of_object (object_of t) and length_of_field f = length_of_typ f.ftype and length_of_comp c = (* union field are considered as struct field *) List.fold_left (fun s f -> s + length_of_field f) 0 c.cfields let position_of_field f = let rec fnext k f = function | [] -> assert false | g::gs -> if Fieldinfo.equal f g then k else fnext (k + length_of_field g) f gs in fnext 0 f f.fcomp.cfields (* -------------------------------------------------------------------------- *) (* --- Utilities on loc-as-term --- *) (* -------------------------------------------------------------------------- *) type sigma = Sigma.t type domain = Sigma.domain type segment = loc rloc let pretty fmt l = F.pp_term fmt l let vars l = F.vars l let occurs x l = F.occurs x l (* -------------------------------------------------------------------------- *) (* --- Generated Axiomatization --- *) (* -------------------------------------------------------------------------- *) let cluster_globals () = Definitions.cluster ~id:"Globals" ~title:"Global Variables" () type shift = | RS_Field of fieldinfo * int (* offset of the field *) | RS_Index of int (* size of the shift *) let phi_base = function | p::_ -> a_base p | _ -> raise Not_found let phi_field offset = function | [p] -> e_add (a_offset p) (F.e_int offset) | _ -> raise Not_found let phi_index size = function | [p;k] -> e_add (a_offset p) (F.e_fact size k) | _ -> raise Not_found module RegisterShift = WpContext.Static (struct type key = lfun type data = shift let name = "MemTyped.RegisterShift" include Lang.Fun end) module ShiftFieldDef = WpContext.StaticGenerator(Cil_datatype.Fieldinfo) (struct let name = "MemTyped.ShiftFieldDef" type key = fieldinfo type data = dfun let generate f = let result = t_addr in let lfun = Lang.generated_f ~result "shiftfield_%s" (Lang.field_id f) in let position = position_of_field f in (* Since its a generated it is the unique name given *) let xloc = Lang.freshvar ~basename:"p" t_addr in let loc = e_var xloc in let def = a_shift loc (F.e_int position) in let dfun = Definitions.Function( result , Def , def) in RegisterShift.define lfun (RS_Field(f,position)) ; MemMemory.register ~base:phi_base ~offset:(phi_field position) lfun ; { d_lfun = lfun ; d_types = 0 ; d_params = [xloc] ; d_definition = dfun ; d_cluster = Definitions.dummy () ; } let compile = Lang.local generate end) module ShiftField = WpContext.Generator(Cil_datatype.Fieldinfo) (struct let name = "MemTyped.ShiftField" type key = fieldinfo type data = lfun let compile fd = let dfun = ShiftFieldDef.get fd in let d_cluster = MemLoader.cluster () in Definitions.define_symbol { dfun with d_cluster } ; dfun.d_lfun end) module Cobj = struct type t = c_object let pretty = C_object.pretty let compare = compare_ptr_conflated end (* This is a model-independent generator, which will be inherited from the model-dependent clusters *) module ShiftGen = WpContext.StaticGenerator(Cobj) (struct let name = "MemTyped.ShiftDef" type key = c_object type data = dfun let rec c_object_id fmt = function | C_int i -> pp_int fmt i | C_float f -> pp_float fmt f | C_pointer _ -> Format.fprintf fmt "PTR" | C_comp c -> Format.pp_print_string fmt (Lang.comp_id c) | C_array a -> let te = object_of a.arr_element in match a.arr_flat with | None -> Format.fprintf fmt "A_%a" c_object_id te | Some f -> Format.fprintf fmt "A%d_%a" f.arr_size c_object_id te let c_object_id c = Format.asprintf "%a@?" c_object_id c let generate obj = let result = t_addr in let shift = Lang.generated_f ~result "shift_%s" (c_object_id obj) in let size = length_of_object obj in (* Since its a generated it is the unique name given *) let xloc = Lang.freshvar ~basename:"p" t_addr in let loc = e_var xloc in let xk = Lang.freshvar ~basename:"k" Qed.Logic.Int in let k = e_var xk in let def = a_shift loc (F.e_fact size k) in let dfun = Definitions.Function( result , Def , def) in RegisterShift.define shift (RS_Index size) ; MemMemory.register ~base:phi_base ~offset:(phi_index size) ~linear:true shift ; { d_lfun = shift ; d_types = 0 ; d_params = [xloc;xk] ; d_definition = dfun ; d_cluster = Definitions.dummy () ; } let compile = Lang.local generate end) (* The model-dependent derivation of model-independent ShiftDef *) module Shift = WpContext.Generator(Cobj) (struct let name = "MemTyped.Shift" type key = c_object type data = lfun let compile obj = let dfun = ShiftGen.get obj in let d_cluster = MemLoader.cluster () in Definitions.define_symbol { dfun with d_cluster } ; dfun.d_lfun end) let field l f = e_fun (ShiftField.get f) [l] let shift l obj k = e_fun (Shift.get obj) [l;k] module LITERAL = struct type t = int * Cstring.cst let compare (a:t) (b:t) = Stdlib.compare (fst a) (fst b) let pretty fmt (eid,cst) = Format.fprintf fmt "%a@%d" Cstring.pretty cst eid end module EID = State_builder.Ref(Datatype.Int) (struct let name = "Wp.MemTyped.EID" let dependencies = [Ast.self] let default () = 0 end) module STRING = WpContext.Generator(LITERAL) (struct let name = "MemTyped.STRING" type key = LITERAL.t type data = term let linked prefix base cst = let name = prefix ^ "_linked" in let a = Lang.freshvar ~basename:"alloc" (Chunk.tau_of_chunk T_alloc) in let m = e_var a in let m_linked = p_call p_linked [m] in let alloc = F.e_get m base in (* The size is alloc-1 *) let sized = Cstring.str_len cst (F.e_add alloc F.e_minus_one) in Definitions.define_lemma { l_kind = `Axiom ; l_name = name ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = p_forall [a] (p_imply m_linked sized) ; l_cluster = Cstring.cluster () ; } let region prefix base cst = let name = prefix ^ "_region" in let re = - Cstring.str_id cst in Definitions.define_lemma { l_kind = `Axiom ; l_name = name ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = p_equal (e_fun f_region [base]) (e_int re) ; l_cluster = Cstring.cluster () ; } let sconst prefix base cst = (** describe the content of literal strings *) let name = prefix ^ "_literal" in let i = Lang.freshvar ~basename:"i" L.Int in let c = Cstring.char_at cst (e_var i) in let addr = shift (a_global base) (C_int (Ctypes.c_char ())) (e_var i) in let m = Lang.freshvar ~basename:"mchar" (Chunk.tau_of_chunk M_char) in let m_sconst = F.p_call p_sconst [e_var m] in let v = F.e_get (e_var m) addr in let read = F.p_equal c v in Definitions.define_lemma { l_kind = `Axiom ; l_name = name ; l_types = 0 ; l_triggers = [] ; l_forall = [m;i] ; l_cluster = Cstring.cluster () ; l_lemma = F.p_imply m_sconst read ; } let fresh () = let eid = succ (EID.get ()) in EID.set eid ; eid let compile (_,cst) = let eid = fresh () in let lfun = Lang.generated_f ~result:L.Int "Str_%d" eid in (* Since its a generated it is the unique name given *) let prefix = Lang.Fun.debug lfun in let base = F.e_fun lfun [] in Definitions.define_symbol { d_lfun = lfun ; d_types = 0 ; d_params = [] ; d_definition = Logic L.Int ; d_cluster = Cstring.cluster () ; } ; Definitions.define_lemma { l_name = prefix ^ "_base" ; l_kind = `Axiom ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = F.p_lt base F.e_zero ; l_cluster = Cstring.cluster () ; } ; region prefix base cst ; linked prefix base cst ; sconst prefix base cst ; base end) (* -------------------------------------------------------------------------- *) (* --- Base Registration --- *) (* -------------------------------------------------------------------------- *) module RegisterBASE = WpContext.Index (struct type key = lfun type data = varinfo let name = "MemTyped.RegisterBASE" include Lang.Fun end) module BASE = WpContext.Generator(Varinfo) (struct let name = "MemTyped.BASE" type key = varinfo type data = term let region prefix x base = let name = prefix ^ "_region" in let re = if x.vglob then 0 else if x.vformal then 1 else 2 in Definitions.define_lemma { l_kind = `Axiom ; l_name = name ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = p_equal (e_fun f_region [base]) (e_int re) ; l_cluster = cluster_globals () ; } let sizeof x = Warning.handle ~handler:(fun _ -> None) ~effect:(Printf.sprintf "No allocation size for variable '%s'" x.vname) (fun obj -> Some (length_of_object obj)) (Ctypes.object_of x.vtype) let linked prefix x base = let name = prefix ^ "_linked" in let size = if x.vglob then sizeof x else Some 0 in match size with | None -> () | Some size -> let a = Lang.freshvar ~basename:"alloc" t_malloc in let m = e_var a in let m_linked = p_call p_linked [m] in let base_size = p_equal (F.e_get m base) (e_int size) in Definitions.define_lemma { l_kind = `Axiom ; l_name = name ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = p_forall [a] (p_imply m_linked base_size) ; l_cluster = cluster_globals () ; } let initialization prefix x base = match sizeof x with | Some size when x.vformal || x.vglob -> let a = Lang.freshvar ~basename:"init" t_init in let m = e_var a in let init_access = if size = 1 then p_bool (F.e_get m (a_addr base e_zero)) else F.p_call p_is_init_r [ m ; a_addr base e_zero ; e_int size ] in let m_init = p_call p_cinits [m] in let init_prop = p_forall [a] (p_imply m_init init_access) in Definitions.define_lemma { l_kind = `Axiom ; l_name = prefix ^ "_init" ; l_types = 0 ; l_triggers = [] ; l_forall = [] ; l_lemma = init_prop ; l_cluster = cluster_globals () ; } | _ -> () let generate x = let acs_rd = Cil.typeHasQualifier "const" x.vtype in let prefix = if x.vglob then if acs_rd then "K" else "G" else if x.vformal then "P" else "L" in let lfun = Lang.generated_f ~category:L.Constructor ~result:L.Int "%s_%s_%d" prefix x.vorig_name x.vid in (* Since its a generated it is the unique name given *) let prefix = Lang.Fun.debug lfun in let vid = if acs_rd then (-x.vid-1) else succ x.vid in let dfun = Definitions.Function( L.Int , Def , e_int vid ) in Definitions.define_symbol { d_lfun = lfun ; d_types = 0 ; d_params = [] ; d_definition = dfun ; d_cluster = cluster_globals () ; } ; let base = e_fun lfun [] in RegisterBASE.define lfun x ; region prefix x base ; linked prefix x base ; initialization prefix x base ; base let compile = Lang.local generate end) (* -------------------------------------------------------------------------- *) (* --- Locations --- *) (* -------------------------------------------------------------------------- *) let null = a_null (* as a loc *) let literal ~eid cst = shift (a_global (STRING.get (eid,cst))) (C_int (Ctypes.c_char ())) e_zero let cvar x = a_global (BASE.get x) let pointer_loc t = t let pointer_val t = t let allocated sigma l = F.e_get (Sigma.value sigma T_alloc) (a_base l) let base_addr l = a_addr (a_base l) e_zero let base_offset l = a_base_offset (a_base l) (a_offset l) let block_length sigma obj l = e_fact (Ctypes.sizeof_object obj) (allocated sigma l) (* -------------------------------------------------------------------------- *) (* --- Cast --- *) (* -------------------------------------------------------------------------- *) module Layout : sig val pretty : Format.formatter -> c_object -> unit val fits: dst:c_object -> src:c_object -> bool (* returns [true] in these cases: - [dst] fits into [src] (exists cobj; [src] = [dst] concat cobj) - [dst] equals [src] ([dst] = [src]) *) end = struct type atom = P of typ | I of c_int | F of c_float let pp_atom fmt = function | P ty -> Printer.pp_typ fmt (TPtr(ty,[])) | I i -> Ctypes.pp_int fmt i | F f -> Ctypes.pp_float fmt f let eq_atom a1 a2 = match a1 , a2 with | P _ , P _ -> true | I i1 , I i2 -> i1 = i2 | F f1 , F f2 -> f1 = f2 | _ -> false type slot = A of atom | S of Cil_types.compinfo (* delayed layout of a C struct *) | U of Cil_types.compinfo (* delayed layout of a C union *) let pp_slot fmt = function | A a -> pp_atom fmt a | S s -> Format.fprintf fmt "{struct %a}" Printer.pp_compinfo s | U u -> Format.fprintf fmt "{union %a}" Printer.pp_compinfo u let eq_slot a1 a2 = (* syntactic equality *) match a1 , a2 with | A a1 , A a2 -> eq_atom a1 a2 | S c1 , S c2 | U c1, U c2 -> Compinfo.equal c1 c2 | _ -> false let rec get_slot = function | C_int i -> A (I i) | C_float f -> A (F f) | C_pointer t -> A (P t) | C_comp ( { cfields = [f] } as c ) -> begin (* union having only one field is equivalent to a struct *) match Ctypes.object_of f.ftype with | C_array _ -> (if c.cstruct then S c else U c) | cobj -> get_slot cobj end | C_comp c -> if c.cstruct then S c else U c | C_array _ -> assert false type block = | Str of slot * int | Arr of c_object * int (* delayed layout of a C type *) | Garbled let pp_block fmt = function | Str(a,n) when n=1 -> pp_slot fmt a | Str(a,n) -> Format.fprintf fmt "%a[%d]" pp_slot a n | Arr(o,n) -> Format.fprintf fmt "{ctype %a}[%d]" Ctypes.pretty o n | Garbled -> Format.fprintf fmt "..." let add_slot a n w = assert (n >= 1) ; match w with | Str(b,m) :: w when eq_slot a b -> Str(b,m+n)::w | _ -> Str(a,n) :: w let add_block p w = match p , w with | Str(a,n) , Str(b,m)::w when eq_slot a b -> Str(b,n+m)::w | Garbled , Garbled::_ -> w | _ -> p :: w type layout = block list let pp_layout fmt = function | [b] -> pp_block fmt b | bs -> begin Format.fprintf fmt "@[{" ; List.iter (fun b -> Format.fprintf fmt "@ %a" pp_block b) bs ; Format.fprintf fmt " }@]" ; end (* requires n > 1 *) let rec add_many cobj n w = (* returns [layout obj]*n @ [w] *) assert (n > 1) ; match cobj, w with | C_array { arr_flat = Some a }, _ when a.arr_cell_nbr = 1 -> add_many (Ctypes.object_of a.arr_cell) n w | C_array _, Arr(o, m)::w when 0 = compare_ptr_conflated o cobj -> Arr(o, m+n)::w | C_array _, _ -> Arr(cobj, n)::w | _ -> add_slot (get_slot cobj) n w let rec rlayout w = function (* returns [layout obj] @ [w] *) | C_array { arr_flat = Some a } -> let cobj = Ctypes.object_of a.arr_cell in if a.arr_cell_nbr = 1 then rlayout w cobj else add_many cobj a.arr_cell_nbr w | C_array { arr_element = e } -> if Wp_parameters.ExternArrays.get () then add_many (Ctypes.object_of e) max_int w else add_block Garbled w | cobj -> add_slot (get_slot cobj) 1 w let layout (obj : c_object) : layout = rlayout [] obj let clayout (c: Cil_types.compinfo) : layout = let flayout w f = rlayout w (Ctypes.object_of f.ftype) in List.fold_left flayout [] (List.rev c.cfields) type comparison = Srem of layout | Drem of layout | Equal | Mismatch let add_array o n w = assert (n > 0) ; if n=1 then rlayout w o else Arr(o, n)::w let decr_slot a n w = assert (n >= 1); if n=1 then w else Str(a, n-1)::w let rec equal u v = match compare ~dst:u ~src:v with | Equal -> true | _ -> false and compare_slot ~dst ~src = match dst, src with | A a1, A a2 -> if eq_atom a1 a2 then Equal else Mismatch | S c1, S c2 | U c1, U c2 when Compinfo.equal c1 c2 -> Equal | S c1, _ -> compare ~dst:(clayout c1) ~src:[Str(src,1)] | _, S c2 -> compare ~dst:[Str(dst,1)] ~src:(clayout c2) | U c1, U c2 -> (* for union, the layouts must be equal *) if equal (clayout c1) (clayout c2) then Equal else Mismatch | U _, A _ -> Mismatch | A _, U _ -> Mismatch and compare ~dst ~src = match dst , src with | [] , [] -> Equal (* src = dst *) | [] , obj -> Srem obj (* src = dst @ obj *) | obj , [] -> Drem obj (* dst = src @ obj *) | p::w1 , q::w2 -> match p , q with | Garbled , _ | _ , Garbled -> Mismatch | Str(a,n) , Str(b,m) -> begin match compare_slot a b with | Mismatch -> Mismatch | Drem a'-> let w1 = a' @ decr_slot a n w1 in let w2 = decr_slot b m w2 in compare w1 w2 | Srem b' -> let w1 = decr_slot a n w1 in let w2 = b' @ decr_slot b m w2 in compare w1 w2 | Equal -> if n < m then let w2 = Str(a,m-n)::w2 in compare w1 w2 else if n > m then let w1 = Str(a,n-m)::w1 in compare w1 w2 else (* n = m *) compare w1 w2 end | Arr(u,n) , Arr(v,m) -> begin match compare ~dst:(layout u) ~src:(layout v) with | Mismatch -> Mismatch | Drem u' -> let w1 = u' @ add_array u (n-1) w1 in let w2 = add_array v (m-1) w2 in compare w1 w2 | Srem v' -> let w1 = add_array u (n-1) w1 in let w2 = v' @ add_array v (m-1) w2 in compare w1 w2 | Equal -> if n < m then let w2 = add_array v (m-n) w2 in compare w1 w2 else if n > m then let w1 = add_array u (n-m) w1 in compare w1 w2 else (* n = m *) compare w1 w2 end | Arr(u,n) , Str _ -> compare ~dst:((layout u) @ add_array u (n-1) w1) ~src | Str _ , Arr(v,n) -> compare ~dst ~src:((layout v) @ add_array v (n-1) w2) let rec repeated ~dst ~src = match dst , src with | [] , [] -> true (* src = dst *) | _ , [] -> false (* empty source layout *) | [] , _ -> false (* empty destination layout *) | [p] , [q] -> begin match p , q with | Garbled , _ | _ , Garbled -> false | Str(a,n) , Str(b,m) -> (* dst =?= repeated(src,n/m) *) begin match compare_slot ~dst:a ~src:b with | Mismatch -> false | Drem a' -> let w1 = a' @ decr_slot a n [] in let w2 = decr_slot b m [] in let cmp = compare ~dst:w1 ~src:w2 in repeated_result ~src cmp | Srem _ -> false | Equal -> (* dst =?= repeated(src,n/m) *) n >= m && (n mod m = 0) end | Arr(u,n) , Arr(v,m) -> begin match compare ~dst:(layout u) ~src:(layout v) with | Mismatch -> false | Drem u' -> let w1 = u' @ add_array u (n-1) [] in let w2 = add_array v (m-1) [] in let cmp = compare ~dst:w1 ~src:w2 in repeated_result ~src cmp | Srem _ -> false | Equal -> (* dst =?= repeated(src,n/m) *) n >= m && (n mod m = 0) end | _ , _ -> repeated_compare ~dst ~src end | _ , _ -> repeated_compare ~dst ~src and repeated_compare ~dst ~src = repeated_result ~src (compare ~dst ~src) and repeated_result ~src = function | Equal -> true | Mismatch | Srem _ -> false | Drem dst -> repeated ~dst ~src let fits ~dst ~src = match dst , src with | C_int i1 , C_int i2 -> i1 = i2 | C_float f1 , C_float f2 -> f1 = f2 | C_comp c , C_comp d when Compinfo.equal c d -> true | C_pointer _ , C_pointer _ -> true | _ -> let src = layout src in match compare ~dst:(layout dst) ~src with | Equal | Srem _ -> true | Mismatch -> false | Drem dst -> repeated dst src let rec pretty fmt = function | C_pointer ty -> Format.fprintf fmt "%a*" pretty (Ctypes.object_of ty) | obj -> pp_layout fmt (layout obj) end let pp_mismatch fmt s = if Context.get pointer <> NoCast && Wp_parameters.has_dkey dkey_layout then Format.fprintf fmt "Cast with incompatible pointers types@\n\ @[@[Source: %a*@]@ @[(layout: %a)@]@]@\n\ @[@[Target: %a*@]@ @[(layout: %a)@]@]" Ctypes.pretty s.pre Layout.pretty s.pre Ctypes.pretty s.post Layout.pretty s.post else Format.fprintf fmt "@[Cast with incompatible pointers types\ @ (source: %a*)@ (target: %a*)@]" Ctypes.pretty s.pre Ctypes.pretty s.post let cast s l = if l==null then null else begin match Context.get pointer with | NoCast -> Warning.error ~source:"Typed Model" "%a" pp_mismatch s | Fits -> if Layout.fits ~dst:s.post ~src:s.pre then l else Warning.error ~source:"Typed Model" "%a" pp_mismatch s | Unsafe -> if not (Layout.fits ~dst:s.post ~src:s.pre) then Warning.emit ~severe:false ~source:"Typed Model" ~effect:"Keep pointer value" "%a" pp_mismatch s ; l end let loc_of_int _ v = F.e_fun f_addr_of_int [v] let int_of_loc _ l = F.e_fun f_int_of_addr [l] (* -------------------------------------------------------------------------- *) (* --- Frames --- *) (* -------------------------------------------------------------------------- *) let frames obj addr = function | T_alloc -> [] | m -> let offset = F.e_int (length_of_object obj) in let sizeof = F.e_one in let tau = Chunk.val_of_chunk m in let basename = Chunk.basename_of_chunk m in MemMemory.frames ~addr ~offset ~sizeof ~basename tau (* -------------------------------------------------------------------------- *) (* --- Chunk element type --- *) (* -------------------------------------------------------------------------- *) module ChunkContent = WpContext.Generator(Chunk) (struct let name = "MemTyped.ChunkContent" type key = Chunk.t type data = lfun let int_kind = function | M_char -> Ctypes.c_char () | M_int k -> k | _ -> failwith "MemTyped asked constraint for non int type" let generate c = let k = int_kind c in let p = Lang.freshvar ~basename:"m" (Chunk.tau_of_chunk c) in let m = e_var p in let name = Format.asprintf "is_%a_chunk" Ctypes.pp_int k in let lfun = Lang.generated_p name in let l = Lang.freshvar ~basename:"l" (Lang.t_addr()) in let is_int = Cint.range k in let def = p_forall [l] (is_int (F.e_get m (e_var l))) in Definitions.define_symbol { d_lfun = lfun ; d_types = 0 ; d_params = [p] ; d_definition = Predicate(Def, def) ; d_cluster = cluster ~id:"Chunk" ~title:"Chunk type constraint" () ; } ; lfun let compile = Lang.local generate end) let is_chunk sigma = function | M_int _ | M_char as m -> F.p_call (ChunkContent.get m) [ Sigma.value sigma m ] | _ -> p_true let is_well_formed sigma = let open Sigma in p_conj (Chunk.Set.fold (fun c l -> is_chunk sigma c :: l) (domain sigma) []) (* -------------------------------------------------------------------------- *) (* --- Loader --- *) (* -------------------------------------------------------------------------- *) module MODEL = struct module Chunk = Chunk module Sigma = Sigma let name = "MemTyped.LOADER" type nonrec loc = loc let field = field let shift = shift let sizeof = length_of_object let init_footprint = init_footprint let value_footprint = value_footprint let frames = frames let to_addr l = l let to_region_pointer l = 0,l let of_region_pointer _ _ l = l let load_int sigma i l = F.e_get (Sigma.value sigma (m_int i)) l let load_float sigma f l = F.e_get (Sigma.value sigma (m_float f)) l let load_pointer sigma _t l = F.e_get (Sigma.value sigma M_pointer) l let last sigma obj l = let n = length_of_object obj in e_sub (F.e_div (allocated sigma l) (F.e_int n)) e_one let havoc obj loc ~length chunk ~fresh ~current = if chunk <> T_alloc then let n = F.e_fact (length_of_object obj) length in F.e_fun f_havoc [fresh;current;loc;n] else fresh let eqmem obj loc _chunk m1 m2 = F.p_call p_eqmem [m1;m2;loc;e_int (length_of_object obj)] let eqmem_forall obj loc _chunk m1 m2 = let xp = Lang.freshvar ~basename:"p" t_addr in let p = F.e_var xp in let n = F.e_int (length_of_object obj) in let separated = F.p_call p_separated [p;e_one;loc;n] in let equal = p_equal (e_get m1 p) (e_get m2 p) in [xp],separated,equal let updated sigma c l v = c , F.e_set (Sigma.value sigma c) l v let store_int sigma i l v = updated sigma (m_int i) l v let store_float sigma f l v = updated sigma (m_float f) l v let store_pointer sigma _ty l v = updated sigma M_pointer l v let set_init_atom sigma l v = updated sigma T_init l v let is_init_atom sigma l = F.e_get (Sigma.value sigma T_init) l let is_init_range sigma obj loc length = let n = F.e_fact (length_of_object obj) length in F.p_call p_is_init_r [ Sigma.value sigma T_init ; loc ; n ] let set_init obj loc ~length _chunk ~current = let n = F.e_fact (length_of_object obj) length in F.e_fun f_set_init [current;loc;n] let monotonic_init s1 s2 = let m1 = Sigma.value s1 T_init in let m2 = Sigma.value s2 T_init in F.p_call p_monotonic [m1; m2] end module LOADER = MemLoader.Make(MODEL) let load = LOADER.load let stored = LOADER.stored let copied = LOADER.copied let assigned = LOADER.assigned let initialized = LOADER.initialized let domain = LOADER.domain (* -------------------------------------------------------------------------- *) (* --- Loc Comparison --- *) (* -------------------------------------------------------------------------- *) let loc_compare f_cmp i_cmp p q = match F.is_equal (a_base p) (a_base q) with | L.Yes -> i_cmp (a_offset p) (a_offset q) | L.Maybe | L.No -> p_call f_cmp [p;q] let is_null l = p_equal l null let loc_eq = p_equal let loc_neq = p_neq let loc_lt = loc_compare p_addr_lt p_lt let loc_leq = loc_compare p_addr_le p_leq let loc_diff obj p q = let delta = e_sub (a_offset p) (a_offset q) in let size = e_int (length_of_object obj) in e_div delta size (* -------------------------------------------------------------------------- *) (* --- Validity --- *) (* -------------------------------------------------------------------------- *) let s_valid sigma acs p n = let p_valid = match acs with | RW -> p_valid_rw | RD -> p_valid_rd | OBJ -> p_valid_obj in p_call p_valid [Sigma.value sigma T_alloc;p;n] let s_invalid sigma p n = p_call p_invalid [Sigma.value sigma T_alloc;p;n] let segment phi = function | Rloc(obj,l) -> phi l (e_int (length_of_object obj)) | Rrange(l,obj,Some a,Some b) -> let l = shift l obj a in let n = e_fact (length_of_object obj) (e_range a b) in phi l n | Rrange(l,_,a,b) -> Wp_parameters.abort ~current:true "Invalid infinite range @[%a+@,(%a@,..%a)@]" F.pp_term l Vset.pp_bound a Vset.pp_bound b let valid sigma acs = segment (s_valid sigma acs) let invalid sigma = segment (s_invalid sigma) let frame sigma = let wellformed_frame phi chunk = if Sigma.mem sigma chunk then [ p_call phi [Sigma.value sigma chunk] ] else [] in wellformed_frame p_linked T_alloc @ wellformed_frame p_cinits T_init @ wellformed_frame p_sconst M_char @ wellformed_frame p_framed M_pointer let alloc sigma xs = if xs = [] then sigma else Sigma.havoc_chunk sigma T_alloc let scope seq scope xs = if xs = [] then [] else let alloc = List.fold_left (fun m x -> let size = match scope with | Sigs.Leave -> 0 | Sigs.Enter -> length_of_typ x.vtype in F.e_set m (BASE.get x) (e_int size)) (Sigma.value seq.pre T_alloc) xs in [ p_equal (Sigma.value seq.post T_alloc) alloc ] let global _sigma p = p_leq (e_fun f_region [a_base p]) e_zero (* -------------------------------------------------------------------------- *) (* --- Segments --- *) (* -------------------------------------------------------------------------- *) let included = let addrof l = l in let sizeof = length_of_object in MemMemory.included ~shift ~addrof ~sizeof let separated = let addrof l = l in let sizeof = length_of_object in MemMemory.separated ~shift ~addrof ~sizeof (* -------------------------------------------------------------------------- *) (* --- State Model --- *) (* -------------------------------------------------------------------------- *) type state = chunk Tmap.t let rec lookup_a e = match F.repr e with | L.Fun( f , [e] ) when f == f_global -> lookup_a e | L.Fun( f , es ) -> lookup_f f es | _ -> raise Not_found and lookup_f f es = try match RegisterShift.find f , es with | RS_Field(fd,_) , [e] -> Mstate.field (lookup_lv e) fd | RS_Index _ , [e;k] -> Mstate.index (lookup_lv e) k | _ -> raise Not_found with Not_found when es = [] -> Sigs.(Mvar (RegisterBASE.find f),[]) and lookup_lv e = try lookup_a e with Not_found -> Sigs.(Mmem e,[]) let mchunk c = match c with | T_init -> Sigs.Mchunk (Pretty_utils.to_string Chunk.pretty c, KInit) | _ -> Sigs.Mchunk (Pretty_utils.to_string Chunk.detailed_pretty c, KValue) let lookup s e = try mchunk (Tmap.find e s) with Not_found -> try match F.repr e with | L.Fun( f , es ) -> Sigs.Maddr (lookup_f f es) | L.Aget( m , k ) when Tmap.find m s = T_init -> Sigs.Mlval (lookup_lv k, KInit) | L.Aget( m , k ) when Tmap.find m s <> T_alloc -> Sigs.Mlval (lookup_lv k, KValue) | _ -> Sigs.Mterm with Not_found -> Sigs.Mterm let apply f s = Tmap.fold (fun m c w -> Tmap.add (f m) c w) s Tmap.empty let iter f s = Tmap.iter (fun m c -> f (mchunk c) m) s let state (sigma : sigma) = let s = ref Tmap.empty in Sigma.iter (fun c x -> s := Tmap.add (e_var x) c !s) sigma ; !s let heap domain state = Tmap.fold (fun m c w -> if Vars.intersect (F.vars m) domain then Heap.Map.add c m w else w ) state Heap.Map.empty let rec diff v1 v2 = if v1 == v2 then Bag.empty else match F.repr v2 with | L.Aset( m , k , v ) -> let lv = lookup_lv k in let upd = Mstore( lv , v ) in Bag.append (diff v1 m) upd | _ -> Bag.empty let updates seq domain = let pool = ref Bag.empty in let pre = heap domain seq.pre in let post = heap domain seq.post in Heap.Map.iter2 (fun chunk v1 v2 -> if chunk <> T_alloc then match v1 , v2 with | Some v1 , Some v2 -> pool := Bag.concat (diff v1 v2) !pool | _ -> ()) pre post ; !pool (* -------------------------------------------------------------------------- *)