(* * Copyright (c) 2009 - 2013 Monoidics ltd. * Copyright (c) 2013 - present Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. *) open! Utils (** Re-arrangement and extension of structures with fresh variables *) module L = Logging module F = Format let list_product l1 l2 = let l1' = IList.rev l1 in let l2' = IList.rev l2 in IList.fold_left (fun acc x -> IList.fold_left (fun acc' y -> (x, y):: acc') acc l2') [] l1' let rec list_rev_and_concat l1 l2 = match l1 with | [] -> l2 | x1:: l1' -> list_rev_and_concat l1' (x1:: l2) (** Check whether the index is out of bounds. If the size is - 1, no check is performed. If the index is provably out of bound, a bound error is given. If the size is a constant and the index is not provably in bound, a warning is given. *) let check_bad_index pname p size index loc = let size_is_constant = match size with | Sil.Const _ -> true | _ -> false in let index_provably_out_of_bound () = let index_too_large = Prop.mk_inequality (Sil.BinOp(Sil.Le, size, index)) in let index_negative = Prop.mk_inequality (Sil.BinOp(Sil.Le, index, Sil.exp_minus_one)) in (Prover.check_atom p index_too_large) || (Prover.check_atom p index_negative) in let index_provably_in_bound () = let size_minus_one = Sil.BinOp(Sil.PlusA, size, Sil.exp_minus_one) in let index_not_too_large = Prop.mk_inequality (Sil.BinOp(Sil.Le, index, size_minus_one)) in let index_nonnegative = Prop.mk_inequality (Sil.BinOp(Sil.Le, Sil.exp_zero, index)) in Prover.check_zero index || (* index 0 always in bound, even when we know nothing about size *) ((Prover.check_atom p index_not_too_large) && (Prover.check_atom p index_nonnegative)) in let index_has_bounds () = match Prover.get_bounds p index with | Some _, Some _ -> true | _ -> false in let get_const_opt = function | Sil.Const (Sil.Cint n) -> Some n | _ -> None in if not (index_provably_in_bound ()) then begin let size_const_opt = get_const_opt size in let index_const_opt = get_const_opt index in if index_provably_out_of_bound () then let deref_str = Localise.deref_str_array_bound size_const_opt index_const_opt in let exn = Exceptions.Array_out_of_bounds_l1 (Errdesc.explain_array_access deref_str p loc, __POS__) in let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop pname) in Reporting.log_warning pname ~pre: pre_opt exn else if size_is_constant then let deref_str = Localise.deref_str_array_bound size_const_opt index_const_opt in let desc = Errdesc.explain_array_access deref_str p loc in let exn = if index_has_bounds () then Exceptions.Array_out_of_bounds_l2 (desc, __POS__) else Exceptions.Array_out_of_bounds_l3 (desc, __POS__) in let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop pname) in Reporting.log_warning pname ~pre: pre_opt exn end (** Perform bounds checking *) let bounds_check pname prop size e = if Config.trace_rearrange then begin L.d_str "Bounds check index:"; Sil.d_exp e; L.d_str " size: "; Sil.d_exp size; L.d_ln() end; check_bad_index pname prop size e let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp t (off: Sil.offset list) inst : Sil.atom list * Sil.strexp * Sil.typ = if Config.trace_rearrange then begin L.d_increase_indent 1; L.d_strln "entering create_struct_values"; L.d_str "typ: "; Sil.d_typ_full t; L.d_ln (); L.d_str "off: "; Sil.d_offset_list off; L.d_ln (); L.d_ln () end; let new_id () = incr max_stamp; Ident.create kind !max_stamp in let res = match t, off with | Sil.Tstruct _, [] -> ([], Sil.Estruct ([], inst), t) | Sil.Tstruct ({ Sil.instance_fields; static_fields } as struct_typ ), (Sil.Off_fld (f, _)):: off' -> let _, t', _ = try IList.find (fun (f', _, _) -> Ident.fieldname_equal f f') (instance_fields @ static_fields) with Not_found -> raise (Exceptions.Bad_footprint __POS__) in let atoms', se', res_t' = create_struct_values pname tenv orig_prop footprint_part kind max_stamp t' off' inst in let se = Sil.Estruct ([(f, se')], inst) in let replace_typ_of_f (f', t', a') = if Ident.fieldname_equal f f' then (f, res_t', a') else (f', t', a') in let instance_fields' = IList.sort Sil.fld_typ_ann_compare (IList.map replace_typ_of_f instance_fields) in (atoms', se, Sil.Tstruct { struct_typ with Sil.instance_fields = instance_fields'}) | Sil.Tstruct _, (Sil.Off_index e):: off' -> let atoms', se', res_t' = create_struct_values pname tenv orig_prop footprint_part kind max_stamp t off' inst in let e' = Sil.array_clean_new_index footprint_part e in let size = Sil.exp_get_undefined false in let se = Sil.Earray (size, [(e', se')], inst) in let res_t = Sil.Tarray (res_t', size) in (Sil.Aeq(e, e'):: atoms', se, res_t) | Sil.Tarray(_, size),[] -> ([], Sil.Earray(size, [], inst), t) | Sil.Tarray(t', size'), (Sil.Off_index e) :: off' -> bounds_check pname orig_prop size' e (State.get_loc ()); let atoms', se', res_t' = create_struct_values pname tenv orig_prop footprint_part kind max_stamp t' off' inst in let e' = Sil.array_clean_new_index footprint_part e in let se = Sil.Earray(size', [(e', se')], inst) in let res_t = Sil.Tarray(res_t', size') in (Sil.Aeq(e, e'):: atoms', se, res_t) | Sil.Tarray _, (Sil.Off_fld _) :: _ -> assert false | Sil.Tint _, [] | Sil.Tfloat _, [] | Sil.Tvoid, [] | Sil.Tfun _, [] | Sil.Tptr _, [] -> let id = new_id () in ([], Sil.Eexp (Sil.Var id, inst), t) | Sil.Tint _, [Sil.Off_index e] | Sil.Tfloat _, [Sil.Off_index e] | Sil.Tvoid, [Sil.Off_index e] | Sil.Tfun _, [Sil.Off_index e] | Sil.Tptr _, [Sil.Off_index e] -> (* In this case, we lift t to the t array. *) let t' = match t with | Sil.Tptr(t', _) -> t' | _ -> t in let size = Sil.Var (new_id ()) in let atoms', se', res_t' = create_struct_values pname tenv orig_prop footprint_part kind max_stamp t' [] inst in let e' = Sil.array_clean_new_index footprint_part e in let se = Sil.Earray(size, [(e', se')], inst) in let res_t = Sil.Tarray(res_t', size) in (Sil.Aeq(e, e'):: atoms', se, res_t) | Sil.Tint _, _ | Sil.Tfloat _, _ | Sil.Tvoid, _ | Sil.Tfun _, _ | Sil.Tptr _, _ -> L.d_str "create_struct_values type:"; Sil.d_typ_full t; L.d_str " off: "; Sil.d_offset_list off; L.d_ln(); raise (Exceptions.Bad_footprint __POS__) | Sil.Tvar _, _ -> L.d_str "create_struct_values type:"; Sil.d_typ_full t; L.d_str " off: "; Sil.d_offset_list off; L.d_ln(); assert false in if Config.trace_rearrange then begin let _, se, _ = res in L.d_strln "exiting create_struct_values, returning"; Sil.d_sexp se; L.d_decrease_indent 1; L.d_ln (); L.d_ln () end; res (** Extend the strexp by populating the path indicated by [off]. This means that it will add missing flds and do the case - analysis for array accesses. This does not catch the array - bounds errors. If we want to implement the checks for array bounds errors, we need to change this function. *) let rec _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se typ (off : Sil.offset list) inst = match off, se, typ with | [], Sil.Eexp _, _ | [], Sil.Estruct _, _ -> [([], se, typ)] | [], Sil.Earray _, _ -> let off_new = Sil.Off_index(Sil.exp_zero):: off in _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se typ off_new inst | (Sil.Off_fld _):: _, Sil.Earray _, Sil.Tarray _ -> let off_new = Sil.Off_index(Sil.exp_zero):: off in _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se typ off_new inst | (Sil.Off_fld (f, _)):: off', Sil.Estruct (fsel, inst'), Sil.Tstruct ({ Sil.instance_fields; static_fields } as struct_typ) -> let replace_fv new_v fv = if Ident.fieldname_equal (fst fv) f then (f, new_v) else fv in let _, typ', _ = try IList.find (fun (f', _, _) -> Ident.fieldname_equal f f') (instance_fields @ static_fields) with Not_found -> raise (Exceptions.Missing_fld (f, __POS__)) in begin try let _, se' = IList.find (fun (f', _) -> Ident.fieldname_equal f f') fsel in let atoms_se_typ_list' = _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se' typ' off' inst in let replace acc (res_atoms', res_se', res_typ') = let replace_fse = replace_fv res_se' in let res_fsel' = IList.sort Sil.fld_strexp_compare (IList.map replace_fse fsel) in let replace_fta (f, t, a) = let f', t' = replace_fv res_typ' (f, t) in (f', t', a) in let instance_fields' = IList.sort Sil.fld_typ_ann_compare (IList.map replace_fta instance_fields) in let struct_typ = Sil.Tstruct { struct_typ with Sil.instance_fields = instance_fields' } in (res_atoms', Sil.Estruct (res_fsel', inst'), struct_typ) :: acc in IList.fold_left replace [] atoms_se_typ_list' with Not_found -> let atoms', se', res_typ' = create_struct_values pname tenv orig_prop footprint_part kind max_stamp typ' off' inst in let res_fsel' = IList.sort Sil.fld_strexp_compare ((f, se'):: fsel) in let replace_fta (f', t', a') = if Ident.fieldname_equal f' f then (f, res_typ', a') else (f', t', a') in let instance_fields' = IList.sort Sil.fld_typ_ann_compare (IList.map replace_fta instance_fields) in let struct_typ = Sil.Tstruct { struct_typ with Sil.instance_fields = instance_fields' } in [(atoms', Sil.Estruct (res_fsel', inst'), struct_typ)] end | (Sil.Off_fld (_, _)):: _, _, _ -> raise (Exceptions.Bad_footprint __POS__) | (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tint _ | (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tfloat _ | (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tvoid | (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tfun _ | (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tptr _ | (Sil.Off_index _):: _, Sil.Estruct _, Sil.Tstruct _ -> (* L.d_strln_color Orange "turn into an array"; *) let size = match se with | Sil.Eexp (_, Sil.Ialloc) -> Sil.exp_one (* if allocated explicitly, we know size is 1 *) | _ -> if Config.type_size then Sil.exp_one (* Sil.Sizeof (typ, Sil.Subtype.exact) *) else Sil.exp_get_undefined false in let se_new = Sil.Earray(size, [(Sil.exp_zero, se)], inst) in let typ_new = Sil.Tarray(typ, size) in _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se_new typ_new off inst | (Sil.Off_index e):: off', Sil.Earray(size, esel, inst_arr), Sil.Tarray(typ', size_for_typ') -> bounds_check pname orig_prop size e (State.get_loc ()); begin try let _, se' = IList.find (fun (e', _) -> Sil.exp_equal e e') esel in let atoms_se_typ_list' = _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se' typ' off' inst in let replace acc (res_atoms', res_se', res_typ') = let replace_ise ise = if Sil.exp_equal e (fst ise) then (e, res_se') else ise in let res_esel' = IList.map replace_ise esel in if (Sil.typ_equal res_typ' typ') || (IList.length res_esel' = 1) then (res_atoms', Sil.Earray(size, res_esel', inst_arr), Sil.Tarray(res_typ', size_for_typ')) :: acc else raise (Exceptions.Bad_footprint __POS__) in IList.fold_left replace [] atoms_se_typ_list' with Not_found -> array_case_analysis_index pname tenv orig_prop footprint_part kind max_stamp size esel size_for_typ' typ' e off' inst_arr inst end | _, _, _ -> raise (Exceptions.Bad_footprint __POS__) and array_case_analysis_index pname tenv orig_prop footprint_part kind max_stamp array_size array_cont typ_array_size typ_cont index off inst_arr inst = let check_sound t' = if not (Sil.typ_equal typ_cont t' || array_cont == []) then raise (Exceptions.Bad_footprint __POS__) in let index_in_array = IList.exists (fun (i, _) -> Prover.check_equal Prop.prop_emp index i) array_cont in let array_is_full = match array_size with | Sil.Const (Sil.Cint n') -> Sil.Int.geq (Sil.Int.of_int (IList.length array_cont)) n' | _ -> false in if index_in_array then let array_default = Sil.Earray(array_size, array_cont, inst_arr) in let typ_default = Sil.Tarray(typ_cont, typ_array_size) in [([], array_default, typ_default)] else if !Config.footprint then begin let atoms, elem_se, elem_typ = create_struct_values pname tenv orig_prop footprint_part kind max_stamp typ_cont off inst in check_sound elem_typ; let cont_new = IList.sort Sil.exp_strexp_compare ((index, elem_se):: array_cont) in let array_new = Sil.Earray(array_size, cont_new, inst_arr) in let typ_new = Sil.Tarray(elem_typ, typ_array_size) in [(atoms, array_new, typ_new)] end else begin let res_new = if array_is_full then [] else begin let atoms, elem_se, elem_typ = create_struct_values pname tenv orig_prop footprint_part kind max_stamp typ_cont off inst in check_sound elem_typ; let cont_new = IList.sort Sil.exp_strexp_compare ((index, elem_se):: array_cont) in let array_new = Sil.Earray(array_size, cont_new, inst_arr) in let typ_new = Sil.Tarray(elem_typ, typ_array_size) in [(atoms, array_new, typ_new)] end in let rec handle_case acc isel_seen_rev = function | [] -> IList.flatten (IList.rev (res_new:: acc)) | (i, se) as ise :: isel_unseen -> let atoms_se_typ_list = _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se typ_cont off inst in let atoms_se_typ_list' = IList.fold_left (fun acc' (atoms', se', typ') -> check_sound typ'; let atoms_new = Sil.Aeq(index, i) :: atoms' in let isel_new = list_rev_and_concat isel_seen_rev ((i, se'):: isel_unseen) in let array_new = Sil.Earray(array_size, isel_new, inst_arr) in let typ_new = Sil.Tarray(typ', typ_array_size) in (atoms_new, array_new, typ_new):: acc' ) [] atoms_se_typ_list in let acc_new = atoms_se_typ_list' :: acc in let isel_seen_rev_new = ise :: isel_seen_rev in handle_case acc_new isel_seen_rev_new isel_unseen in handle_case [] [] array_cont end let exp_has_only_footprint_ids e = let fav = Sil.exp_fav e in Sil.fav_filter_ident fav (fun id -> not (Ident.is_footprint id)); Sil.fav_is_empty fav let laundry_offset_for_footprint max_stamp offs_in = let rec laundry offs_seen eqs offs = match offs with | [] -> (IList.rev offs_seen, IList.rev eqs) | (Sil.Off_fld _ as off):: offs' -> let offs_seen' = off:: offs_seen in laundry offs_seen' eqs offs' | (Sil.Off_index(idx) as off):: offs' -> if exp_has_only_footprint_ids idx then let offs_seen' = off:: offs_seen in laundry offs_seen' eqs offs' else let () = incr max_stamp in let fid_new = Ident.create Ident.kfootprint !max_stamp in let exp_new = Sil.Var fid_new in let off_new = Sil.Off_index exp_new in let offs_seen' = off_new:: offs_seen in let eqs' = (fid_new, idx):: eqs in laundry offs_seen' eqs' offs' in laundry [] [] offs_in let strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se te (off : Sil.offset list) inst = let typ = Sil.texp_to_typ None te in let off', laundry_atoms = let off', eqs = laundry_offset_for_footprint max_stamp off in (* do laundry_offset whether footprint_part is true or not, so max_stamp is modified anyway *) if footprint_part then off', IList.map (fun (id, e) -> Prop.mk_eq (Sil.Var id) e) eqs else off, [] in if Config.trace_rearrange then (L.d_str "entering strexp_extend_values se: "; Sil.d_sexp se; L.d_str " typ: "; Sil.d_typ_full typ; L.d_str " off': "; Sil.d_offset_list off'; L.d_strln (if footprint_part then " FP" else " RE")); let atoms_se_typ_list = _strexp_extend_values pname tenv orig_prop footprint_part kind max_stamp se typ off' inst in let atoms_se_typ_list_filtered = let neg_atom = function Sil.Aeq(e1, e2) -> Sil.Aneq(e1, e2) | Sil.Aneq(e1, e2) -> Sil.Aeq(e1, e2) in let check_neg_atom atom = Prover.check_atom Prop.prop_emp (neg_atom atom) in let check_not_inconsistent (atoms, _, _) = not (IList.exists check_neg_atom atoms) in IList.filter check_not_inconsistent atoms_se_typ_list in if Config.trace_rearrange then L.d_strln "exiting strexp_extend_values"; let st = match te with | Sil.Sizeof(_, st) -> st | _ -> Sil.Subtype.exact in IList.map (fun (atoms', se', typ') -> (laundry_atoms @ atoms', se', Sil.Sizeof (typ', st))) atoms_se_typ_list_filtered let collect_root_offset exp = let root = Sil.root_of_lexp exp in let offsets = Sil.exp_get_offsets exp in (root, offsets) (** Sil.Construct a points-to predicate for an expression, to add to a footprint. *) let mk_ptsto_exp_footprint pname tenv orig_prop (lexp, typ) max_stamp inst : Sil.hpred * Sil.hpred * Sil.atom list = let root, off = collect_root_offset lexp in if not (exp_has_only_footprint_ids root) then begin (* in angelic mode, purposely ignore dangling pointer warnings during the footprint phase -- we * will fix them during the re - execution phase *) if not (Config.angelic_execution && !Config.footprint) then begin if Config.developer_mode then L.err "!!!! Footprint Error, Bad Root : %a !!!! @\n" (Sil.pp_exp pe_text) lexp; let deref_str = Localise.deref_str_dangling None in let err_desc = Errdesc.explain_dereference deref_str orig_prop (State.get_loc ()) in raise (Exceptions.Dangling_pointer_dereference (None, err_desc, __POS__)) end end; let off_foot, eqs = laundry_offset_for_footprint max_stamp off in let st = match !Config.curr_language with | Config.Clang -> Sil.Subtype.exact | Config.Java -> Sil.Subtype.subtypes in let create_ptsto footprint_part off0 = match root, off0, typ with | Sil.Lvar pvar, [], Sil.Tfun _ -> let fun_name = Procname.from_string_c_fun (Mangled.to_string (Pvar.get_name pvar)) in let fun_exp = Sil.Const (Sil.Cfun fun_name) in ([], Prop.mk_ptsto root (Sil.Eexp (fun_exp, inst)) (Sil.Sizeof (typ, st))) | _, [], Sil.Tfun _ -> let atoms, se, t = create_struct_values pname tenv orig_prop footprint_part Ident.kfootprint max_stamp typ off0 inst in (atoms, Prop.mk_ptsto root se (Sil.Sizeof (t, st))) | _ -> let atoms, se, t = create_struct_values pname tenv orig_prop footprint_part Ident.kfootprint max_stamp typ off0 inst in (atoms, Prop.mk_ptsto root se (Sil.Sizeof (t, st))) in let atoms, ptsto_foot = create_ptsto true off_foot in let sub = Sil.sub_of_list eqs in let ptsto = Sil.hpred_sub sub ptsto_foot in let atoms' = IList.map (fun (id, e) -> Prop.mk_eq (Sil.Var id) e) eqs in (ptsto, ptsto_foot, atoms @ atoms') (** Check if the path in exp exists already in the current ptsto predicate. If it exists, return None. Otherwise, return [Some fld] with [fld] the missing field. *) let prop_iter_check_fields_ptsto_shallow iter lexp = let offset = Sil.exp_get_offsets lexp in let (_, se, _) = match Prop.prop_iter_current iter with | Sil.Hpointsto (e, se, t), _ -> (e, se, t) | _ -> assert false in let rec check_offset se = function | [] -> None | (Sil.Off_fld (fld, _)):: off' -> (match se with | Sil.Estruct (fsel, _) -> (try let _, se' = IList.find (fun (fld', _) -> Sil.fld_equal fld fld') fsel in check_offset se' off' with Not_found -> Some fld) | _ -> Some fld) | (Sil.Off_index _):: _ -> None in check_offset se offset let fav_max_stamp fav = let max_stamp = ref 0 in let f id = max_stamp := max !max_stamp (Ident.get_stamp id) in IList.iter f (Sil.fav_to_list fav); max_stamp (** [prop_iter_extend_ptsto iter lexp] extends the current psto predicate in [iter] with enough fields to follow the path in [lexp] -- field splitting model. It also materializes all indices accessed in lexp. *) let prop_iter_extend_ptsto pname tenv orig_prop iter lexp inst = if Config.trace_rearrange then (L.d_str "entering prop_iter_extend_ptsto lexp: "; Sil.d_exp lexp; L.d_ln ()); let offset = Sil.exp_get_offsets lexp in let max_stamp = fav_max_stamp (Prop.prop_iter_fav iter) in let max_stamp_val = !max_stamp in let extend_footprint_pred = function | Sil.Hpointsto(e, se, te) -> let atoms_se_te_list = strexp_extend_values pname tenv orig_prop true Ident.kfootprint (ref max_stamp_val) se te offset inst in IList.map (fun (atoms', se', te') -> (atoms', Sil.Hpointsto (e, se', te'))) atoms_se_te_list | Sil.Hlseg (k, hpara, e1, e2, el) -> begin match hpara.Sil.body with | Sil.Hpointsto(e', se', te'):: body_rest -> let atoms_se_te_list = strexp_extend_values pname tenv orig_prop true Ident.kfootprint (ref max_stamp_val) se' te' offset inst in let atoms_body_list = IList.map (fun (atoms0, se0, te0) -> (atoms0, Sil.Hpointsto(e', se0, te0):: body_rest)) atoms_se_te_list in let atoms_hpara_list = IList.map (fun (atoms, body') -> (atoms, { hpara with Sil.body = body'})) atoms_body_list in IList.map (fun (atoms, hpara') -> (atoms, Sil.Hlseg(k, hpara', e1, e2, el))) atoms_hpara_list | _ -> assert false end | _ -> assert false in let atoms_se_te_to_iter e (atoms, se, te) = let iter' = IList.fold_left (Prop.prop_iter_add_atom !Config.footprint) iter atoms in Prop.prop_iter_update_current iter' (Sil.Hpointsto (e, se, te)) in let do_extend e se te = if Config.trace_rearrange then begin L.d_strln "entering do_extend"; L.d_str "e: "; Sil.d_exp e; L.d_str " se : "; Sil.d_sexp se; L.d_str " te: "; Sil.d_texp_full te; L.d_ln (); L.d_ln () end; let extend_kind = match e with (* Determine whether to extend the footprint part or just the normal part *) | Sil.Var id when not (Ident.is_footprint id) -> Ident.kprimed | Sil.Lvar pvar when Pvar.is_local pvar -> Ident.kprimed | _ -> Ident.kfootprint in let iter_list = let atoms_se_te_list = strexp_extend_values pname tenv orig_prop false extend_kind max_stamp se te offset inst in IList.map (atoms_se_te_to_iter e) atoms_se_te_list in let res_iter_list = if Ident.kind_equal extend_kind Ident.kprimed then iter_list (* normal part already extended: nothing to do *) else (* extend footprint part *) let atoms_fp_sigma_list = let footprint_sigma = Prop.prop_iter_get_footprint_sigma iter in let sigma_pto, sigma_rest = IList.partition (function | Sil.Hpointsto(e', _, _) -> Sil.exp_equal e e' | Sil.Hlseg (_, _, e1, _, _) -> Sil.exp_equal e e1 | Sil.Hdllseg (_, _, e_iF, _, _, e_iB, _) -> Sil.exp_equal e e_iF || Sil.exp_equal e e_iB ) footprint_sigma in let atoms_sigma_list = match sigma_pto with | [hpred] -> let atoms_hpred_list = extend_footprint_pred hpred in IList.map (fun (atoms, hpred') -> (atoms, hpred' :: sigma_rest)) atoms_hpred_list | _ -> L.d_warning "Cannot extend "; Sil.d_exp lexp; L.d_strln " in"; Prop.d_prop (Prop.prop_iter_to_prop iter); L.d_ln(); [([], footprint_sigma)] in IList.map (fun (atoms, sigma') -> (atoms, IList.stable_sort Sil.hpred_compare sigma')) atoms_sigma_list in let iter_atoms_fp_sigma_list = list_product iter_list atoms_fp_sigma_list in IList.map (fun (iter, (atoms, fp_sigma)) -> let iter' = IList.fold_left (Prop.prop_iter_add_atom !Config.footprint) iter atoms in Prop.prop_iter_replace_footprint_sigma iter' fp_sigma ) iter_atoms_fp_sigma_list in let res_prop_list = IList.map Prop.prop_iter_to_prop res_iter_list in begin L.d_str "in prop_iter_extend_ptsto lexp: "; Sil.d_exp lexp; L.d_ln (); L.d_strln "prop before:"; let prop_before = Prop.prop_iter_to_prop iter in Prop.d_prop prop_before; L.d_ln (); L.d_ln (); L.d_ln (); L.d_strln "prop list after:"; Propgraph.d_proplist prop_before res_prop_list; L.d_ln (); L.d_ln (); L.d_ln (); res_iter_list end in begin match Prop.prop_iter_current iter with | Sil.Hpointsto (e, se, te), _ -> do_extend e se te | _ -> assert false end (** Add a pointsto for [root(lexp): typ] to the sigma and footprint of a prop, if it's compatible with the allowed footprint variables. Then, change it into a iterator. This function ensures that [root(lexp): typ] is the current hpred of the iterator. typ is the type of the root of lexp. *) let prop_iter_add_hpred_footprint_to_prop pname tenv prop (lexp, typ) inst = let max_stamp = fav_max_stamp (Prop.prop_footprint_fav prop) in let ptsto, ptsto_foot, atoms = mk_ptsto_exp_footprint pname tenv prop (lexp, typ) max_stamp inst in L.d_strln "++++ Adding footprint frame"; Prop.d_prop (Prop.prop_hpred_star Prop.prop_emp ptsto); L.d_ln (); L.d_ln (); let eprop = Prop.expose prop in let foot_sigma = ptsto_foot :: Prop.get_sigma_footprint eprop in let nfoot_sigma = Prop.sigma_normalize_prop Prop.prop_emp foot_sigma in let prop' = Prop.normalize (Prop.replace_sigma_footprint nfoot_sigma eprop) in let prop_new = IList.fold_left (Prop.prop_atom_and ~footprint:!Config.footprint) prop' atoms in let iter = match (Prop.prop_iter_create prop_new) with | None -> let prop_new' = Prop.normalize (Prop.prop_hpred_star prop_new ptsto) in begin match (Prop.prop_iter_create prop_new') with | None -> assert false | Some iter -> iter end | Some iter -> Prop.prop_iter_prev_then_insert iter ptsto in let offsets_default = Sil.exp_get_offsets lexp in Prop.prop_iter_set_state iter offsets_default (* TODO: have to call this on both reads and writes *) let add_guarded_by_constraints prop lexp = let sigma = Prop.get_sigma prop in (** if [fld] is annotated with @GuardedBy("mLock"), return mLock *) let get_guarded_by_fld_str fld typ = let extract_guarded_by_str (annot, _) = if Annotations.annot_ends_with annot Annotations.guarded_by then match annot.Sil.parameters with | [guarded_by_str] -> Some guarded_by_str | _ -> None else None in match Annotations.get_field_type_and_annotation fld typ with | Some (_, item_annot) -> IList.find_map_opt extract_guarded_by_str item_annot | _ -> None in (* find A.guarded_by_fld_str |-> B and return Some B, or None if there is no such hpred *) let find_guarded_by_exp fld typ sigma = match get_guarded_by_fld_str fld typ with | Some guarded_by_fld_str -> let extract_guarded_by_strexp (fld, strexp) = (* this comparison needs to be somewhat fuzzy, since programmers are free to write @GuardedBy("mLock"), @GuardedBy("MyClass.mLock"), or use other conventions *) if Ident.fieldname_to_flat_string fld = guarded_by_fld_str || Ident.fieldname_to_string fld = guarded_by_fld_str then Some strexp else None in IList.find_map_opt (function | Sil.Hpointsto (_, Estruct (flds, _), _) -> IList.find_map_opt extract_guarded_by_strexp flds | _ -> None) sigma | None -> None in let check_fld_locks typ prop_acc (fld, strexp) = match strexp with | Sil.Eexp (exp, _) when Sil.exp_equal exp lexp -> begin match find_guarded_by_exp fld typ sigma with | Some (Sil.Eexp (guarded_by_exp, _)) -> let has_lock = IList.exists (function | Sil.Alocked -> true | _ -> false) (Prop.get_exp_attributes prop_acc guarded_by_exp) in if has_lock then (* we have the lock; no need to add a proof obligation *) (* TODO: materialize [fld], but don't add [fld] to the footprint. *) prop_acc else (* the lock is not known to be held. add a "lock held" proof obligation *) (* TODO: materialize [fld], but don't add [fld] to the footprint. *) let locked_attr = Sil.Const (Cattribute Alocked) in Prop.conjoin_neq ~footprint:true guarded_by_exp locked_attr prop_acc | _ -> (* field not guarded; proceed with abduction as we normally would *) prop_acc end | _ -> prop_acc in let hpred_check_flds prop_acc = function | Sil.Hpointsto (_, Estruct (flds, _), Sizeof (typ, _)) -> IList.fold_left (check_fld_locks typ) prop_acc flds | _ -> prop_acc in IList.fold_left hpred_check_flds prop sigma (** Add a pointsto for [root(lexp): typ] to the iterator and to the footprint, if it's compatible with the allowed footprint variables. This function ensures that [root(lexp): typ] is the current hpred of the iterator. typ is the type of the root of lexp. *) let prop_iter_add_hpred_footprint pname tenv orig_prop iter (lexp, typ) inst = let max_stamp = fav_max_stamp (Prop.prop_iter_footprint_fav iter) in let ptsto, ptsto_foot, atoms = mk_ptsto_exp_footprint pname tenv orig_prop (lexp, typ) max_stamp inst in L.d_strln "++++ Adding footprint frame"; Prop.d_prop (Prop.prop_hpred_star Prop.prop_emp ptsto); L.d_ln (); L.d_ln (); let foot_sigma = ptsto_foot :: (Prop.prop_iter_get_footprint_sigma iter) in let iter_foot = Prop.prop_iter_prev_then_insert iter ptsto in let iter_foot_atoms = IList.fold_left (Prop.prop_iter_add_atom (!Config.footprint)) iter_foot atoms in let iter' = Prop.prop_iter_replace_footprint_sigma iter_foot_atoms foot_sigma in let offsets_default = Sil.exp_get_offsets lexp in Prop.prop_iter_set_state iter' offsets_default exception ARRAY_ACCESS let rearrange_arith lexp prop = if Config.trace_rearrange then begin L.d_strln "entering rearrange_arith"; L.d_str "lexp: "; Sil.d_exp lexp; L.d_ln (); L.d_str "prop: "; L.d_ln (); Prop.d_prop prop; L.d_ln (); L.d_ln () end; if (Config.array_level >= 2) then raise ARRAY_ACCESS else let root = Sil.root_of_lexp lexp in if Prover.check_allocatedness prop root then raise ARRAY_ACCESS else raise (Exceptions.Symexec_memory_error __POS__) let pp_rearrangement_error message prop lexp = L.d_strln (".... Rearrangement Error .... " ^ message); L.d_str "Exp:"; Sil.d_exp lexp; L.d_ln (); L.d_str "Prop:"; L.d_ln (); Prop.d_prop prop; L.d_ln (); L.d_ln () (** do re-arrangment for an iter whose current element is a pointsto *) let iter_rearrange_ptsto pname tenv orig_prop iter lexp inst = if Config.trace_rearrange then begin L.d_increase_indent 1; L.d_strln "entering iter_rearrange_ptsto"; L.d_str "lexp: "; Sil.d_exp lexp; L.d_ln (); L.d_strln "prop:"; Prop.d_prop orig_prop; L.d_ln (); L.d_strln "iter:"; Prop.d_prop (Prop.prop_iter_to_prop iter); L.d_ln (); L.d_ln () end; let check_field_splitting () = match prop_iter_check_fields_ptsto_shallow iter lexp with | None -> () | Some fld -> begin pp_rearrangement_error "field splitting check failed" orig_prop lexp; raise (Exceptions.Missing_fld (fld, __POS__)) end in let res = if !Config.footprint then prop_iter_extend_ptsto pname tenv orig_prop iter lexp inst else begin check_field_splitting (); match Prop.prop_iter_current iter with | Sil.Hpointsto (e, se, te), offset -> let max_stamp = fav_max_stamp (Prop.prop_iter_fav iter) in let atoms_se_te_list = strexp_extend_values pname tenv orig_prop false Ident.kprimed max_stamp se te offset inst in let handle_case (atoms', se', te') = let iter' = IList.fold_left (Prop.prop_iter_add_atom !Config.footprint) iter atoms' in Prop.prop_iter_update_current iter' (Sil.Hpointsto (e, se', te')) in let filter it = let p = Prop.prop_iter_to_prop it in not (Prover.check_inconsistency p) in IList.filter filter (IList.map handle_case atoms_se_te_list) | _ -> [iter] end in begin if Config.trace_rearrange then begin L.d_strln "exiting iter_rearrange_ptsto, returning results"; Prop.d_proplist_with_typ (IList.map Prop.prop_iter_to_prop res); L.d_decrease_indent 1; L.d_ln (); L.d_ln () end; res end (** do re-arrangment for an iter whose current element is a nonempty listseg *) let iter_rearrange_ne_lseg recurse_on_iters iter para e1 e2 elist = if Config.nelseg then let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_inst1) = Sil.hpara_instantiate para e1 n' elist in let hpred_list1 = para_inst1@[Prop.mk_lseg Sil.Lseg_NE para n' e2 elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_base_case = let (_, para_inst) = Sil.hpara_instantiate para e1 e2 elist in Prop.prop_iter_update_current_by_list iter para_inst in recurse_on_iters [iter_inductive_case; iter_base_case] else let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_inst1) = Sil.hpara_instantiate para e1 n' elist in let hpred_list1 = para_inst1@[Prop.mk_lseg Sil.Lseg_PE para n' e2 elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in recurse_on_iters [iter_inductive_case] (** do re-arrangment for an iter whose current element is a nonempty dllseg to be unrolled from lhs *) let iter_rearrange_ne_dllseg_first recurse_on_iters iter para_dll e1 e2 e3 e4 elist = let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_dll_inst1) = Sil.hpara_dll_instantiate para_dll e1 e2 n' elist in let hpred_list1 = para_dll_inst1@[Prop.mk_dllseg Sil.Lseg_NE para_dll n' e1 e3 e4 elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_base_case = let (_, para_dll_inst) = Sil.hpara_dll_instantiate para_dll e1 e2 e3 elist in let iter' = Prop.prop_iter_update_current_by_list iter para_dll_inst in let prop' = Prop.prop_iter_to_prop iter' in let prop'' = Prop.conjoin_eq ~footprint: (!Config.footprint) e1 e4 prop' in match (Prop.prop_iter_create prop'') with | None -> assert false | Some iter' -> iter' in recurse_on_iters [iter_inductive_case; iter_base_case] (** do re-arrangment for an iter whose current element is a nonempty dllseg to be unrolled from rhs *) let iter_rearrange_ne_dllseg_last recurse_on_iters iter para_dll e1 e2 e3 e4 elist = let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_dll_inst1) = Sil.hpara_dll_instantiate para_dll e4 n' e3 elist in let hpred_list1 = para_dll_inst1@[Prop.mk_dllseg Sil.Lseg_NE para_dll e1 e2 e4 n' elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_base_case = let (_, para_dll_inst) = Sil.hpara_dll_instantiate para_dll e4 e2 e3 elist in let iter' = Prop.prop_iter_update_current_by_list iter para_dll_inst in let prop' = Prop.prop_iter_to_prop iter' in let prop'' = Prop.conjoin_eq ~footprint: (!Config.footprint) e1 e4 prop' in match (Prop.prop_iter_create prop'') with | None -> assert false | Some iter' -> iter' in recurse_on_iters [iter_inductive_case; iter_base_case] (** do re-arrangment for an iter whose current element is a possibly empty listseg *) let iter_rearrange_pe_lseg recurse_on_iters default_case_iter iter para e1 e2 elist = let iter_nonemp_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_inst1) = Sil.hpara_instantiate para e1 n' elist in let hpred_list1 = para_inst1@[Prop.mk_lseg Sil.Lseg_PE para n' e2 elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_subcases = let removed_prop = Prop.prop_iter_remove_curr_then_to_prop iter in let prop' = Prop.conjoin_eq ~footprint: (!Config.footprint) e1 e2 removed_prop in match (Prop.prop_iter_create prop') with | None -> let iter' = default_case_iter (Prop.prop_iter_set_state iter ()) in [Prop.prop_iter_set_state iter' ()] | Some iter' -> [iter_nonemp_case; iter'] in recurse_on_iters iter_subcases (** do re-arrangment for an iter whose current element is a possibly empty dllseg to be unrolled from lhs *) let iter_rearrange_pe_dllseg_first recurse_on_iters default_case_iter iter para_dll e1 e2 e3 e4 elist = let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_dll_inst1) = Sil.hpara_dll_instantiate para_dll e1 e2 n' elist in let hpred_list1 = para_dll_inst1@[Prop.mk_dllseg Sil.Lseg_PE para_dll n' e1 e3 e4 elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_subcases = let removed_prop = Prop.prop_iter_remove_curr_then_to_prop iter in let prop' = Prop.conjoin_eq ~footprint: (!Config.footprint) e1 e3 removed_prop in let prop'' = Prop.conjoin_eq ~footprint: (!Config.footprint) e2 e4 prop' in match (Prop.prop_iter_create prop'') with | None -> let iter' = default_case_iter (Prop.prop_iter_set_state iter ()) in [Prop.prop_iter_set_state iter' ()] | Some iter' -> [iter_inductive_case; iter'] in recurse_on_iters iter_subcases (** do re-arrangment for an iter whose current element is a possibly empty dllseg to be unrolled from rhs *) let iter_rearrange_pe_dllseg_last recurse_on_iters default_case_iter iter para_dll e1 e2 e3 e4 elist = let iter_inductive_case = let n' = Sil.Var (Ident.create_fresh Ident.kprimed) in let (_, para_dll_inst1) = Sil.hpara_dll_instantiate para_dll e4 n' e3 elist in let hpred_list1 = para_dll_inst1@[Prop.mk_dllseg Sil.Lseg_PE para_dll e1 e2 e4 n' elist] in Prop.prop_iter_update_current_by_list iter hpred_list1 in let iter_subcases = let removed_prop = Prop.prop_iter_remove_curr_then_to_prop iter in let prop' = Prop.conjoin_eq ~footprint: (!Config.footprint) e1 e3 removed_prop in let prop'' = Prop.conjoin_eq ~footprint: (!Config.footprint) e2 e4 prop' in match (Prop.prop_iter_create prop'') with | None -> let iter' = default_case_iter (Prop.prop_iter_set_state iter ()) in [Prop.prop_iter_set_state iter' ()] | Some iter' -> [iter_inductive_case; iter'] in recurse_on_iters iter_subcases (** find the type at the offset from the given type expression, if any *) let type_at_offset texp off = let rec strip_offset off typ = match off, typ with | [], _ -> Some typ | (Sil.Off_fld (f, _)):: off', Sil.Tstruct { Sil.instance_fields } -> (try let typ' = (fun (_, y, _) -> y) (IList.find (fun (f', _, _) -> Ident.fieldname_equal f f') instance_fields) in strip_offset off' typ' with Not_found -> None) | (Sil.Off_index _):: off', Sil.Tarray (typ', _) -> strip_offset off' typ' | _ -> None in match texp with | Sil.Sizeof(typ, _) -> strip_offset off typ | _ -> None (** Check that the size of a type coming from an instruction does not exceed the size of the type from the pointsto predicate For example, that a pointer to int is not used to assign to a char *) let check_type_size pname prop texp off typ_from_instr = L.d_strln_color Orange "check_type_size"; L.d_str "off: "; Sil.d_offset_list off; L.d_ln (); L.d_str "typ_from_instr: "; Sil.d_typ_full typ_from_instr; L.d_ln (); match type_at_offset texp off with | Some typ_of_object -> L.d_str "typ_o: "; Sil.d_typ_full typ_of_object; L.d_ln (); if Prover.type_size_comparable typ_from_instr typ_of_object && Prover.check_type_size_leq typ_from_instr typ_of_object = false then begin let deref_str = Localise.deref_str_pointer_size_mismatch typ_from_instr typ_of_object in let loc = State.get_loc () in let exn = Exceptions.Pointer_size_mismatch ( Errdesc.explain_dereference deref_str prop loc, __POS__) in let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop pname) in Reporting.log_warning pname ~pre: pre_opt exn end | None -> L.d_str "texp: "; Sil.d_texp_full texp; L.d_ln () (** Exposes lexp |->- from iter. In case that it is not possible to * expose lexp |->-, this function prints an error message and * faults. There are four things to note. First, typ is the type of the * root of lexp. Second, prop should mean the same as iter. Third, the * result [] means that the given input iter is inconsistent. This * happens when the theorem prover can prove the inconsistency of prop, * only after unrolling some predicates in prop. This function ensures * that the theorem prover cannot prove the inconsistency of any of the * new iters in the result. *) let rec iter_rearrange pname tenv lexp typ_from_instr prop iter inst: (Sil.offset list) Prop.prop_iter list = let typ = match Sil.exp_get_offsets lexp with | Sil.Off_fld (f, ((Sil.Tstruct _) as struct_typ)) :: _ -> (* access through field: get the struct type from the field *) if Config.trace_rearrange then begin L.d_increase_indent 1; L.d_str "iter_rearrange: root of lexp accesses field "; L.d_strln (Ident.fieldname_to_string f); L.d_str " type from instruction: "; Sil.d_typ_full typ_from_instr; L.d_ln(); L.d_str " struct type from field: "; Sil.d_typ_full struct_typ; L.d_ln(); L.d_decrease_indent 1; L.d_ln(); end; struct_typ | _ -> typ_from_instr in if Config.trace_rearrange then begin L.d_increase_indent 1; L.d_strln "entering iter_rearrange"; L.d_str "lexp: "; Sil.d_exp lexp; L.d_ln (); L.d_str "typ: "; Sil.d_typ_full typ; L.d_ln (); L.d_strln "prop:"; Prop.d_prop prop; L.d_ln (); L.d_strln "iter:"; Prop.d_prop (Prop.prop_iter_to_prop iter); L.d_ln (); L.d_ln () end; let default_case_iter (iter': unit Prop.prop_iter) = if Config.trace_rearrange then L.d_strln "entering default_case_iter"; if !Config.footprint then prop_iter_add_hpred_footprint pname tenv prop iter' (lexp, typ) inst else if (Config.array_level >= 1 && not !Config.footprint && Sil.exp_pointer_arith lexp) then rearrange_arith lexp prop else begin pp_rearrangement_error "cannot find predicate with root" prop lexp; if not !Config.footprint then Printer.force_delayed_prints (); raise (Exceptions.Symexec_memory_error __POS__) end in let recurse_on_iters iters = let f_one_iter iter' = let prop' = Prop.prop_iter_to_prop iter' in if Prover.check_inconsistency prop' then [] else iter_rearrange pname tenv (Prop.lexp_normalize_prop prop' lexp) typ prop' iter' inst in let rec f_many_iters iters_lst = function | [] -> IList.flatten (IList.rev iters_lst) | iter':: iters' -> let iters_res' = f_one_iter iter' in f_many_iters (iters_res':: iters_lst) iters' in f_many_iters [] iters in let filter = function | Sil.Hpointsto (base, _, _) | Sil.Hlseg (_, _, base, _, _) -> Prover.is_root prop base lexp | Sil.Hdllseg (_, _, first, _, _, last, _) -> let result_first = Prover.is_root prop first lexp in match result_first with | None -> Prover.is_root prop last lexp | Some _ -> result_first in let res = match Prop.prop_iter_find iter filter with | None -> [default_case_iter iter] | Some iter -> match Prop.prop_iter_current iter with | (Sil.Hpointsto (_, _, texp), off) -> if Config.type_size then check_type_size pname prop texp off typ_from_instr; iter_rearrange_ptsto pname tenv prop iter lexp inst | (Sil.Hlseg (Sil.Lseg_NE, para, e1, e2, elist), _) -> iter_rearrange_ne_lseg recurse_on_iters iter para e1 e2 elist | (Sil.Hlseg (Sil.Lseg_PE, para, e1, e2, elist), _) -> iter_rearrange_pe_lseg recurse_on_iters default_case_iter iter para e1 e2 elist | (Sil.Hdllseg (Sil.Lseg_NE, para_dll, e1, e2, e3, e4, elist), _) -> begin match Prover.is_root prop e1 lexp, Prover.is_root prop e4 lexp with | None, None -> assert false | Some _, _ -> iter_rearrange_ne_dllseg_first recurse_on_iters iter para_dll e1 e2 e3 e4 elist | _, Some _ -> iter_rearrange_ne_dllseg_last recurse_on_iters iter para_dll e1 e2 e3 e4 elist end | (Sil.Hdllseg (Sil.Lseg_PE, para_dll, e1, e2, e3, e4, elist), _) -> begin match Prover.is_root prop e1 lexp, Prover.is_root prop e4 lexp with | None, None -> assert false | Some _, _ -> iter_rearrange_pe_dllseg_first recurse_on_iters default_case_iter iter para_dll e1 e2 e3 e4 elist | _, Some _ -> iter_rearrange_pe_dllseg_last recurse_on_iters default_case_iter iter para_dll e1 e2 e3 e4 elist end in if Config.trace_rearrange then begin L.d_strln "exiting iter_rearrange, returning results"; Prop.d_proplist_with_typ (IList.map Prop.prop_iter_to_prop res); L.d_decrease_indent 1; L.d_ln (); L.d_ln () end; res (** Check for dereference errors: dereferencing 0, a freed value, or an undefined value *) let check_dereference_error pdesc (prop : Prop.normal Prop.t) lexp loc = let nullable_obj_str = ref None in (* return true if deref_exp is only pointed to by fields/params with @Nullable annotations *) let is_only_pt_by_nullable_fld_or_param deref_exp = let ann_sig = Models.get_modelled_annotated_signature (Specs.pdesc_resolve_attributes pdesc) in IList.for_all (fun hpred -> match hpred with | Sil.Hpointsto (Sil.Lvar pvar, Sil.Eexp (Sil.Var _ as exp, _), _) when Sil.exp_equal exp deref_exp -> let is_nullable = if Annotations.param_is_nullable pvar ann_sig then begin nullable_obj_str := Some (Pvar.to_string pvar); true end else let is_nullable_attr = function | Sil.Aretval (pname, ret_attr) | Sil.Aundef (pname, ret_attr, _, _) when Annotations.ia_is_nullable ret_attr -> nullable_obj_str := Some (Procname.to_string pname); true | _ -> false in IList.exists is_nullable_attr (Prop.get_exp_attributes prop exp) in (* it's ok for a non-nullable local to point to deref_exp *) is_nullable || Pvar.is_local pvar | Sil.Hpointsto (_, Sil.Estruct (flds, _), Sil.Sizeof (typ, _)) -> let fld_is_nullable fld = match Annotations.get_field_type_and_annotation fld typ with | Some (_, annot) -> Annotations.ia_is_nullable annot | _ -> false in let is_strexp_pt_by_nullable_fld (fld, strexp) = match strexp with | Sil.Eexp (Sil.Var _ as exp, _) when Sil.exp_equal exp deref_exp -> let is_nullable = fld_is_nullable fld in if is_nullable then nullable_obj_str := Some (Ident.fieldname_to_simplified_string fld); is_nullable | _ -> true in IList.for_all is_strexp_pt_by_nullable_fld flds | _ -> true) (Prop.get_sigma prop) && !nullable_obj_str <> None in let root = Sil.root_of_lexp lexp in let is_deref_of_nullable = let is_definitely_non_null exp prop = Prover.check_disequal prop exp Sil.exp_zero in Config.report_nullable_inconsistency && not (is_definitely_non_null root prop) && is_only_pt_by_nullable_fld_or_param root in let relevant_attributes_getters = [ Prop.get_resource_attribute; Prop.get_undef_attribute; ] in let get_relevant_attributes exp = let rec fold_getters = function | [] -> None | getter:: tl -> match getter prop exp with | Some _ as some_attr -> some_attr | None -> fold_getters tl in fold_getters relevant_attributes_getters in let attribute_opt = match get_relevant_attributes root with | Some att -> Some att | None -> (* try to remove an offset if any, and find the attribute there *) let root_no_offset = match root with | Sil.BinOp((Sil.PlusPI | Sil.PlusA | Sil.MinusPI | Sil.MinusA), base, _) -> base | _ -> root in get_relevant_attributes root_no_offset in if Prover.check_zero (Sil.root_of_lexp root) || is_deref_of_nullable then begin let deref_str = if is_deref_of_nullable then match !nullable_obj_str with | Some str -> Localise.deref_str_nullable None str | None -> Localise.deref_str_nullable None "" else Localise.deref_str_null None in let err_desc = Errdesc.explain_dereference ~use_buckets: true ~is_nullable: is_deref_of_nullable deref_str prop loc in if Localise.is_parameter_not_null_checked_desc err_desc then raise (Exceptions.Parameter_not_null_checked (err_desc, __POS__)) else if Localise.is_field_not_null_checked_desc err_desc then raise (Exceptions.Field_not_null_checked (err_desc, __POS__)) else if (Localise.is_empty_vector_access_desc err_desc) then raise (Exceptions.Empty_vector_access (err_desc, __POS__)) else raise (Exceptions.Null_dereference (err_desc, __POS__)) end; match attribute_opt with | Some (Sil.Adangling dk) -> let deref_str = Localise.deref_str_dangling (Some dk) in let err_desc = Errdesc.explain_dereference deref_str prop (State.get_loc ()) in raise (Exceptions.Dangling_pointer_dereference (Some dk, err_desc, __POS__)) | Some (Sil.Aundef (s, _, undef_loc, _)) -> if Config.angelic_execution then () else let deref_str = Localise.deref_str_undef (s, undef_loc) in let err_desc = Errdesc.explain_dereference deref_str prop loc in raise (Exceptions.Skip_pointer_dereference (err_desc, __POS__)) | Some (Sil.Aresource ({ Sil.ra_kind = Sil.Rrelease } as ra)) -> let deref_str = Localise.deref_str_freed ra in let err_desc = Errdesc.explain_dereference ~use_buckets: true deref_str prop loc in raise (Exceptions.Use_after_free (err_desc, __POS__)) | _ -> if Prover.check_equal Prop.prop_emp (Sil.root_of_lexp root) Sil.exp_minus_one then let deref_str = Localise.deref_str_dangling None in let err_desc = Errdesc.explain_dereference deref_str prop loc in raise (Exceptions.Dangling_pointer_dereference (None, err_desc, __POS__)) (* Check that an expression representin an objc block can be null and raise a [B1] null exception.*) (* It's used to check that we don't call possibly null blocks *) let check_call_to_objc_block_error pdesc prop fun_exp loc = let fun_exp_may_be_null () = (* may be null if we don't know if it is definitely not null *) not (Prover.check_disequal prop (Sil.root_of_lexp fun_exp) Sil.exp_zero) in let try_explaining_exp e = (* when e is a temp var, try to find the pvar defining e*) match e with | Sil.Var id -> (match (Errdesc.find_ident_assignment (State.get_node ()) id) with | Some (_, e') -> e' | None -> e) | _ -> e in let get_exp_called () = (* Exp called in the block's function call*) match State.get_instr () with | Some Sil.Call(_, Sil.Var id, _, _, _) -> Errdesc.find_ident_assignment (State.get_node ()) id | _ -> None in let is_fun_exp_captured_var () = (* Called expression is a captured variable of the block *) match get_exp_called () with | Some (_, Sil.Lvar pvar) -> (* pvar is the block *) let name = Pvar.get_name pvar in IList.exists (fun (cn, _) -> (Mangled.to_string name) = (Mangled.to_string cn)) (Cfg.Procdesc.get_captured pdesc) | _ -> false in let is_field_deref () = (*Called expression is a field *) match get_exp_called () with | Some (_, (Sil.Lfield(e', fn, t))) -> let e'' = try_explaining_exp e' in Some (Sil.Lfield(e'', fn, t)), true (* the block dereferences is a field of an object*) | Some (_, e) -> Some e, false | _ -> None, false in if (!Config.curr_language = Config.Clang) && fun_exp_may_be_null () && not (is_fun_exp_captured_var ()) then begin let deref_str = Localise.deref_str_null None in let err_desc_nobuckets = Errdesc.explain_dereference ~is_nullable: true deref_str prop loc in match fun_exp with | Sil.Var id when Ident.is_footprint id -> let e_opt, is_field_deref = is_field_deref () in let err_desc_nobuckets' = (match e_opt with | Some e -> Localise.parameter_field_not_null_checked_desc err_desc_nobuckets e | _ -> err_desc_nobuckets) in let err_desc = Localise.error_desc_set_bucket err_desc_nobuckets' Localise.BucketLevel.b1 Config.show_buckets in if is_field_deref then raise (Exceptions.Field_not_null_checked (err_desc, __POS__)) else raise (Exceptions.Parameter_not_null_checked (err_desc, __POS__)) | _ -> (* HP: fun_exp is not a footprint therefore, either is a local or it's a modified param *) let err_desc = Localise.error_desc_set_bucket err_desc_nobuckets Localise.BucketLevel.b1 Config.show_buckets in raise (Exceptions.Null_dereference (err_desc, __POS__)) end (** [rearrange lexp prop] rearranges [prop] into the form [prop' * lexp|->strexp:typ]. It returns an iterator with [lexp |-> strexp: typ] as current predicate and the path (an [offsetlist]) which leads to [lexp] as the iterator state. *) let rearrange ?(report_deref_errors=true) pdesc tenv lexp typ prop loc : (Sil.offset list) Prop.prop_iter list = let nlexp = match Prop.exp_normalize_prop prop lexp with | Sil.BinOp(Sil.PlusPI, ep, e) -> (* array access with pointer arithmetic *) Sil.Lindex(ep, e) | e -> e in let ptr_tested_for_zero = Prover.check_disequal prop (Sil.root_of_lexp nlexp) Sil.exp_zero in let inst = Sil.inst_rearrange (not ptr_tested_for_zero) loc (State.get_path_pos ()) in L.d_strln ".... Rearrangement Start ...."; L.d_str "Exp: "; Sil.d_exp nlexp; L.d_ln (); L.d_str "Prop: "; L.d_ln(); Prop.d_prop prop; L.d_ln (); L.d_ln (); if report_deref_errors then check_dereference_error pdesc prop nlexp (State.get_loc ()); let pname = Cfg.Procdesc.get_proc_name pdesc in let prop' = if Config.csl_analysis && !Config.footprint && Procname.is_java pname then add_guarded_by_constraints prop lexp else prop in match Prop.prop_iter_create prop' with | None -> if !Config.footprint then [prop_iter_add_hpred_footprint_to_prop pname tenv prop' (nlexp, typ) inst] else begin pp_rearrangement_error "sigma is empty" prop nlexp; raise (Exceptions.Symexec_memory_error __POS__) end | Some iter -> iter_rearrange pname tenv nlexp typ prop' iter inst (* let pp_off fmt off = IList.iter (fun n -> match n with | Sil.Off_fld (f, t) -> F.fprintf fmt "%a " Ident.pp_fieldname f | Sil.Off_index e -> F.fprintf fmt "%a " (Sil.pp_exp pe_text) e) off let sort_ftl ftl = let compare (f1, _) (f2, _) = Sil.fld_compare f1 f2 in IList.sort compare ftl *)