[clang] add sizeof static value to translation

Summary:
Modify the type of `Exp.Sizeof ...` to include the value that the expression
evaluates to according to the compiler, or None if it cannot be known
statically.

Use this information in inferbo.

Mostly unused in the BiAbduction checker for now, although it could be useful
there too.

update-submodule: facebook-clang-plugins

Reviewed By: mbouaziz

Differential Revision: D4953634

fbshipit-source-id: be0999d
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent 1c23876a4e
commit 1b0ee6fbc3

@ -1 +1 @@
Subproject commit 21d4222b325650c67944a4ad9cc2b72c7e37fefd
Subproject commit 259d4e518b31b1d8c78c744c87b5bbe4ba775882

@ -22,9 +22,18 @@ type _ident = Ident.t;
let compare__ident x y => Ident.compare y x;
type closure = {name: Typ.Procname.t, captured_vars: list (t, Pvar.t, Typ.t)} [@@deriving compare]
/** dynamically determined length of an array value, if any */
and dynamic_length = option t [@@deriving compare]
type closure = {name: Typ.Procname.t, captured_vars: list (t, Pvar.t, Typ.t)}
/** This records information about a [sizeof(typ)] expression.
[nbytes] represents the result of the evaluation of [sizeof(typ)] if it is statically known.
If [typ] is of the form [Tarray elt (Some static_length)], then [dynamic_length] is the number
of elements of type [elt] in the array. The [dynamic_length], tracked by symbolic execution, may
differ from the [static_length] obtained from the type definition, e.g. when an array is
over-allocated.
If [typ] is a struct type, the [dynamic_length] is that of the final extensible array, if any.*/
and sizeof_data = {typ: Typ.t, nbytes: option int, dynamic_length: option t, subtype: Subtype.t}
/** Program expressions. */
and t =
/** Pure variable: it is not an lvalue */
@ -47,12 +56,8 @@ and t =
| Lfield t Fieldname.t Typ.t
/** An array index offset: [exp1\[exp2\]] */
| Lindex t t
/** A sizeof expression. [Sizeof (Tarray elt (Some static_length)) (Some dynamic_length)]
represents the size of an array value consisting of [dynamic_length] elements of type [elt].
The [dynamic_length], tracked by symbolic execution, may differ from the [static_length]
obtained from the type definition, e.g. when an array is over-allocated. For struct types,
the [dynamic_length] is that of the final extensible array, if any. */
| Sizeof Typ.t dynamic_length Subtype.t;
| Sizeof sizeof_data
[@@deriving compare];
let equal = [%compare.equal : t];
@ -105,7 +110,7 @@ let is_zero =
If not a sizeof, return the default type if given, otherwise raise an exception */
let texp_to_typ default_opt =>
fun
| Sizeof t _ _ => t
| Sizeof {typ} => typ
| _ => Typ.unsome "texp_to_typ" default_opt;
@ -200,10 +205,8 @@ let get_vars exp => {
init::vars
captured_vars
| Const (Cint _ | Cfun _ | Cstr _ | Cfloat _ | Cclass _ | Cptr_to_fld _) => vars
/* TODO: Sizeof length expressions may contain variables, do not ignore them. */
/* | Sizeof _ None _ => vars */
/* | Sizeof _ (Some l) _ => get_vars_ l vars */
| Sizeof _ _ _ => vars
/* TODO: Sizeof dynamic length expressions may contain variables, do not ignore them. */
| Sizeof _ => vars
};
get_vars_ exp ([], [])
};
@ -238,9 +241,10 @@ let rec pp_ pe pp_t f e => {
| Lvar pv => Pvar.pp pe f pv
| Lfield e fld _ => F.fprintf f "%a.%a" pp_exp e Fieldname.pp fld
| Lindex e1 e2 => F.fprintf f "%a[%a]" pp_exp e1 pp_exp e2
| Sizeof t l s =>
| Sizeof {typ, nbytes, dynamic_length, subtype} =>
let pp_len f l => Option.iter f::(F.fprintf f "[%a]" pp_exp) l;
F.fprintf f "sizeof(%a%a%a)" pp_t t pp_len l Subtype.pp s
let pp_size f size => Option.iter f::(Int.pp f) size;
F.fprintf f "sizeof(%a%a%a%a)" pp_t typ pp_size nbytes pp_len dynamic_length Subtype.pp subtype
}
};

@ -15,9 +15,18 @@ module L = Logging;
module F = Format;
type closure = {name: Typ.Procname.t, captured_vars: list (t, Pvar.t, Typ.t)} [@@deriving compare]
/** dynamically determined length of an array value, if any */
and dynamic_length = option t [@@deriving compare]
type closure = {name: Typ.Procname.t, captured_vars: list (t, Pvar.t, Typ.t)}
/** This records information about a [sizeof(typ)] expression.
[nbytes] represents the result of the evaluation of [sizeof(typ)] if it is statically known.
If [typ] is of the form [Tarray elt (Some static_length)], then [dynamic_length] is the number
of elements of type [elt] in the array. The [dynamic_length], tracked by symbolic execution, may
differ from the [static_length] obtained from the type definition, e.g. when an array is
over-allocated.
If [typ] is a struct type, the [dynamic_length] is that of the final extensible array, if any.*/
and sizeof_data = {typ: Typ.t, nbytes: option int, dynamic_length: option t, subtype: Subtype.t}
/** Program expressions. */
and t =
/** Pure variable: it is not an lvalue */
@ -40,12 +49,7 @@ and t =
| Lfield t Fieldname.t Typ.t
/** An array index offset: [exp1\[exp2\]] */
| Lindex t t
/** A sizeof expression. [Sizeof (Tarray elt (Some static_length)) (Some dynamic_length)]
represents the size of an array value consisting of [dynamic_length] elements of type [elt].
The [dynamic_length], tracked by symbolic execution, may differ from the [static_length]
obtained from the type definition, e.g. when an array is over-allocated. For struct types,
the [dynamic_length] is that of the final extensible array, if any. */
| Sizeof Typ.t dynamic_length Subtype.t
| Sizeof sizeof_data
[@@deriving compare];

@ -76,8 +76,8 @@ let rec of_sil ~f_resolve_id (exp : Exp.t) typ = match exp with
Constant c
| Cast (cast_typ, e) ->
Cast (cast_typ, of_sil ~f_resolve_id e typ)
| Sizeof (sizeof_typ, dynamic_length, _) ->
Sizeof (sizeof_typ, Option.map ~f:(fun e -> of_sil ~f_resolve_id e typ) dynamic_length)
| Sizeof {typ; dynamic_length} ->
Sizeof (typ, Option.map ~f:(fun e -> of_sil ~f_resolve_id e typ) dynamic_length)
| Closure closure ->
let environment =
List.map

@ -61,7 +61,7 @@ let of_sil ~f_resolve_id (instr : Sil.instr) =
when Pvar.is_ssa_frontend_tmp lhs_pvar ->
analyze_id_assignment (Var.of_pvar lhs_pvar) rhs_exp lhs_typ loc
| Call (Some (ret_id, _), Const (Cfun callee_pname),
(target_exp, _) :: (Sizeof (cast_typ, _, _), _) :: _ , loc, _)
(target_exp, _) :: (Sizeof {typ=cast_typ}, _) :: _ , loc, _)
when Typ.Procname.equal callee_pname BuiltinDecl.__cast ->
analyze_id_assignment (Var.of_id ret_id) target_exp cast_typ loc
| Store (lhs_exp, typ, rhs_exp, loc) ->

@ -771,7 +771,7 @@ let desc_leak hpred_type_opt value_str_opt resource_opt resource_action_opt loc
MF.monospaced_to_string s, " to ", " on " in
let typ_str =
match hpred_type_opt with
| Some (Exp.Sizeof ({desc=Tstruct name}, _, _)) when Typ.Name.is_class name ->
| Some (Exp.Sizeof {typ={desc=Tstruct name}}) when Typ.Name.is_class name ->
" of type " ^ MF.monospaced_to_string (Typ.Name.name name) ^ " "
| _ -> " " in
let desc_str =
@ -861,7 +861,7 @@ let desc_retain_cycle cycle loc cycle_dotty =
str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") object "^e_str^" retaining " ^
MF.monospaced_to_string (e_str^"."^(Fieldname.to_string f))^", ";
ct:=!ct +1
| Sil.Eexp (Exp.Sizeof (typ, _, _), _) ->
| Sil.Eexp (Exp.Sizeof {typ}, _) ->
let step =
" (" ^ (string_of_int !ct) ^ ") an object of " ^
MF.monospaced_to_string (Typ.to_string typ) ^

@ -229,7 +229,7 @@ let hpred_get_lhs h =>
/** {2 Comparision and Inspection Functions} */
let has_objc_ref_counter tenv hpred =>
switch hpred {
| Hpointsto _ _ (Sizeof {desc: Tstruct name} _ _) =>
| Hpointsto _ _ (Sizeof {typ: {desc: Tstruct name}}) =>
switch (Tenv.lookup tenv name) {
| Some {fields} => List.exists f::Typ.Struct.is_objc_ref_counter_field fields
| _ => false
@ -383,9 +383,11 @@ let d_exp_list (el: list Exp.t) => L.add_print_action (L.PTexp_list, Obj.repr el
let pp_texp pe f =>
fun
| Exp.Sizeof t l s => {
| Exp.Sizeof {typ, nbytes, dynamic_length, subtype} => {
let pp_len f l => Option.iter f::(F.fprintf f "[%a]" (pp_exp_printenv pe)) l;
F.fprintf f "%a%a%a" (Typ.pp pe) t pp_len l Subtype.pp s
let pp_size f size => Option.iter f::(Int.pp f) size;
F.fprintf
f "%a%a%a%a" (Typ.pp pe) typ pp_size nbytes pp_len dynamic_length Subtype.pp subtype
}
| e => (pp_exp_printenv pe) f e;
@ -393,9 +395,11 @@ let pp_texp pe f =>
/** Pretty print a type with all the details. */
let pp_texp_full pe f =>
fun
| Exp.Sizeof t l s => {
| Exp.Sizeof {typ, nbytes, dynamic_length, subtype} => {
let pp_len f l => Option.iter f::(F.fprintf f "[%a]" (pp_exp_printenv pe)) l;
F.fprintf f "%a%a%a" (Typ.pp_full pe) t pp_len l Subtype.pp s
let pp_size f size => Option.iter f::(Int.pp f) size;
F.fprintf
f "%a%a%a%a" (Typ.pp_full pe) typ pp_size nbytes pp_len dynamic_length Subtype.pp subtype
}
| e => Exp.pp_printenv pe Typ.pp_full f e;
@ -1246,9 +1250,7 @@ let rec exp_fpv e =>
| Lfield e _ _ => exp_fpv e
| Lindex e1 e2 => exp_fpv e1 @ exp_fpv e2
/* TODO: Sizeof length expressions may contain variables, do not ignore them. */
/* | Sizeof _ None _ => [] */
/* | Sizeof _ (Some l) _ => exp_fpv l */
| Sizeof _ _ _ => []
| Sizeof _ => []
};
let exp_list_fpv el => List.concat_map f::exp_fpv el;
@ -1443,9 +1445,7 @@ let rec exp_fav_add fav e =>
exp_fav_add fav e1;
exp_fav_add fav e2
/* TODO: Sizeof length expressions may contain variables, do not ignore them. */
/* | Sizeof _ None _ => () */
/* | Sizeof _ (Some l) _ => exp_fav_add fav l; */
| Sizeof _ _ _ => ()
| Sizeof _ => ()
};
let exp_fav = fav_imperative_to_functional exp_fav_add;
@ -1878,17 +1878,14 @@ let rec exp_sub_ids (f: Ident.t => Exp.t) exp =>
} else {
Exp.Lindex e1' e2'
}
| Sizeof t l_opt s =>
switch l_opt {
| Some l =>
let l' = exp_sub_ids f l;
if (phys_equal l' l) {
exp
} else {
Exp.Sizeof t (Some l') s
}
| None => exp
| Sizeof ({dynamic_length: Some l} as sizeof_data) =>
let l' = exp_sub_ids f l;
if (phys_equal l' l) {
exp
} else {
Exp.Sizeof {...sizeof_data, dynamic_length: Some l'}
}
| Sizeof {dynamic_length: None} => exp
};
let rec apply_sub subst id =>
@ -2353,8 +2350,8 @@ let exp_get_offsets exp => {
| Exn _
| Closure _
| Lvar _
| Sizeof _ None _ => offlist_past
| Sizeof _ (Some l) _ => f offlist_past l
| Sizeof {dynamic_length: None} => offlist_past
| Sizeof {dynamic_length: Some l} => f offlist_past l
| Cast _ sub_exp => f offlist_past sub_exp
| Lfield sub_exp fldname typ => f [Off_fld fldname typ, ...offlist_past] sub_exp
| Lindex sub_exp e => f [Off_index e, ...offlist_past] sub_exp

@ -243,8 +243,8 @@ let find_arithmetic_problem tenv proc_node_session prop exp =
| Exp.Lvar _ -> ()
| Exp.Lfield (e, _, _) -> walk e
| Exp.Lindex (e1, e2) -> walk e1; walk e2
| Exp.Sizeof (_, None, _) -> ()
| Exp.Sizeof (_, Some len, _) -> walk len in
| Exp.Sizeof {dynamic_length=None} -> ()
| Exp.Sizeof {dynamic_length=Some len} -> walk len in
walk exp;
let problem_opt =
match (List.find ~f:check_zero !exps_divided, !uminus_unsigned) with

@ -79,7 +79,8 @@ let add_array_to_prop tenv pdesc prop_ lexp typ =
let len = Exp.Var (Ident.create_fresh Ident.kfootprint) in
let s = mk_empty_array_rearranged len in
let hpred =
Prop.mk_ptsto tenv n_lexp s (Exp.Sizeof (arr_typ, Some len, Subtype.exact)) in
Prop.mk_ptsto tenv n_lexp s (Exp.Sizeof {typ=arr_typ; nbytes=None;
dynamic_length=None; subtype=Subtype.exact}) in
let sigma = prop.Prop.sigma in
let sigma_fp = prop.Prop.sigma_fp in
let prop'= Prop.set prop ~sigma:(hpred:: sigma) in
@ -158,13 +159,15 @@ let create_type tenv n_lexp typ prop =
match typ.Typ.desc with
| Typ.Tptr (typ', _) ->
let sexp = Sil.Estruct ([], Sil.inst_none) in
let texp = Exp.Sizeof (typ', None, Subtype.subtypes) in
let texp = Exp.Sizeof {typ=typ'; nbytes=None;
dynamic_length=None; subtype=Subtype.subtypes} in
let hpred = Prop.mk_ptsto tenv n_lexp sexp texp in
Some hpred
| Typ.Tarray _ ->
let len = Exp.Var (Ident.create_fresh Ident.kfootprint) in
let sexp = mk_empty_array len in
let texp = Exp.Sizeof (typ, None, Subtype.subtypes) in
let texp = Exp.Sizeof {typ; nbytes=None;
dynamic_length=None; subtype=Subtype.subtypes} in
let hpred = Prop.mk_ptsto tenv n_lexp sexp texp in
Some hpred
| _ -> None in
@ -553,7 +556,7 @@ let execute___release_autorelease_pool
| Sil.Hpointsto(e1, _, _) -> Exp.equal e1 exp
| _ -> false) prop_.Prop.sigma |>
Option.value_map ~f:(function
| Sil.Hpointsto (_, _, Exp.Sizeof (typ, _, _)) ->
| Sil.Hpointsto (_, _, Exp.Sizeof {typ}) ->
let res1 =
execute___objc_release
{ builtin_args with
@ -750,18 +753,19 @@ let execute_alloc mk can_return_null
Exp.BinOp (bop, evaluate_char_sizeof e1', evaluate_char_sizeof e2')
| Exp.Exn _ | Exp.Closure _ | Exp.Const _ | Exp.Cast _ | Exp.Lvar _ | Exp.Lfield _
| Exp.Lindex _ -> e
| Exp.Sizeof ({Typ.desc=Tarray ({Typ.desc=Tint ik}, _)}, Some len, _) when Typ.ikind_is_char ik ->
| Exp.Sizeof {typ={Typ.desc=Tarray ({Typ.desc=Tint ik}, _)}; dynamic_length=Some len}
when Typ.ikind_is_char ik ->
evaluate_char_sizeof len
| Exp.Sizeof ({Typ.desc=Tarray ({Typ.desc=Tint ik}, Some len)}, None, _) when Typ.ikind_is_char ik ->
| Exp.Sizeof {typ={Typ.desc=Tarray ({Typ.desc=Tint ik}, Some len)}; dynamic_length=None}
when Typ.ikind_is_char ik ->
evaluate_char_sizeof (Exp.Const (Const.Cint len))
| Exp.Sizeof _ -> e in
let size_exp, procname = match args with
| [(Exp.Sizeof ({Typ.desc=Tstruct (ObjcClass _ as name)} as s, len, subt), _)] ->
let struct_type =
match AttributesTable.get_correct_type_from_objc_class_name name with
| Some struct_type -> struct_type
| None -> s in
Exp.Sizeof (struct_type, len, subt), pname
| [(Exp.Sizeof ({typ={Typ.desc=Tstruct (ObjcClass _ as name)}} as sizeof_data) as e, _)] ->
let e' = match AttributesTable.get_correct_type_from_objc_class_name name with
| Some struct_type -> Exp.Sizeof {sizeof_data with typ=struct_type}
| None -> e in
e', pname
| [(size_exp, _)] -> (* for malloc and __new *)
size_exp, PredSymb.mem_alloc_pname mk
| [(size_exp, _); (Exp.Const (Const.Cfun pname), _)] ->
@ -776,7 +780,8 @@ let execute_alloc mk can_return_null
let n_size_exp' = evaluate_char_sizeof n_size_exp in
Prop.exp_normalize_prop tenv prop n_size_exp', prop in
let cnt_te =
Exp.Sizeof (Typ.mk (Tarray (Typ.mk (Tint Typ.IChar), None)), Some size_exp', Subtype.exact) in
Exp.Sizeof {typ=Typ.mk (Tarray (Typ.mk (Tint Typ.IChar), None));
nbytes=None; dynamic_length=Some size_exp'; subtype=Subtype.exact} in
let id_new = Ident.create_fresh Ident.kprimed in
let exp_new = Exp.Var id_new in
let ptsto_new =
@ -811,7 +816,7 @@ let execute___cxx_typeid ({ Builtin.pdesc; tenv; prop_; args; loc} as r)
| Sil.Hpointsto (e, _, _) -> Exp.equal e n_lexp
| _ -> false) prop.Prop.sigma |>
Option.value_map ~f:(function
| Sil.Hpointsto (_, _, Exp.Sizeof (dynamic_type, _, _)) -> dynamic_type
| Sil.Hpointsto (_, _, Exp.Sizeof {typ}) -> typ
| _ -> typ_
)
~default:typ_ in
@ -954,7 +959,7 @@ let execute_objc_alloc_no_fail
{ Builtin.pdesc; tenv; ret_id; loc; } =
let alloc_fun = Exp.Const (Const.Cfun BuiltinDecl.__objc_alloc_no_fail) in
let ptr_typ = Typ.mk (Tptr (typ, Typ.Pk_pointer)) in
let sizeof_typ = Exp.Sizeof (typ, None, Subtype.exact) in
let sizeof_typ = Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
let alloc_fun_exp =
match alloc_fun_opt with
| Some pname -> [Exp.Const (Const.Cfun pname), Typ.mk Tvoid]

@ -416,7 +416,7 @@ let typ_get_recursive_flds tenv typ_exp =
false
in
match typ_exp with
| Exp.Sizeof (typ, _, _) -> (
| Exp.Sizeof {typ} -> (
match typ.desc with
| Tstruct name -> (
match Tenv.lookup tenv name with
@ -900,14 +900,14 @@ let get_cycle root prop =
let visited' = (fst et_src):: visited in
let res = (match get_points_to e with
| None -> path, false
| Some (Sil.Hpointsto (_, Sil.Estruct (fl, _), Exp.Sizeof (te, _, _))) ->
| Some (Sil.Hpointsto (_, Sil.Estruct (fl, _), Exp.Sizeof {typ=te})) ->
dfs e_root (e, te) ((et_src, f, e):: path) fl visited'
| _ -> path, false (* check for lists *)) in
if snd res then res
else dfs e_root et_src path el' visited') in
L.d_strln "Looking for cycle with root expression: "; Sil.d_hpred root; L.d_strln "";
match root with
| Sil.Hpointsto (e_root, Sil.Estruct (fl, _), Exp.Sizeof (te, _, _)) ->
| Sil.Hpointsto (e_root, Sil.Estruct (fl, _), Exp.Sizeof {typ=te}) ->
let se_root = Sil.Eexp(e_root, Sil.Inone) in
(* start dfs with empty path and expr pointing to root *)
let (pot_cycle, res) = dfs se_root (se_root, te) [] fl [] in
@ -926,7 +926,7 @@ let get_cycle root prop =
let should_raise_objc_leak hpred =
match hpred with
| Sil.Hpointsto(_, Sil.Estruct((fn, Sil.Eexp( (Exp.Const (Const.Cint i)), _)):: _, _),
Exp.Sizeof (typ, _, _))
Exp.Sizeof {typ})
when Fieldname.is_hidden fn && IntLit.gt i IntLit.zero (* counter > 0 *) ->
Mleak_buckets.should_raise_objc_leak typ
| _ -> None
@ -946,7 +946,7 @@ let get_var_retain_cycle prop_ =
| _ -> false in
let is_hpred_block v h =
match h, v with
| Sil.Hpointsto (e, _, Exp.Sizeof (typ, _, _)), Sil.Eexp (e', _)
| Sil.Hpointsto (e, _, Exp.Sizeof {typ}), Sil.Eexp (e', _)
when Exp.equal e e' && Typ.is_block_type typ -> true
| _, _ -> false in
let find v =
@ -962,7 +962,9 @@ let get_var_retain_cycle prop_ =
| Some pvar -> [((sexp pvar, t), f, e')]
| _ -> (match find_block e with
| Some blk -> [((sexp blk, t), f, e')]
| _ -> [((sexp (Exp.Sizeof (t, None, Subtype.exact)), t), f, e')]) in
| _ ->
let sizeof = {Exp.typ=t; nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
[((sexp (Exp.Sizeof sizeof), t), f, e')]) in
(* returns the pvars of the first cycle we find in sigma.
This is an heuristic that works if there is one cycle.
In case there are more than one cycle we may return not necessarily

@ -834,10 +834,10 @@ let rec exp_construct_fresh side e =
let e1' = exp_construct_fresh side e1 in
let e2' = exp_construct_fresh side e2 in
Exp.Lindex(e1', e2')
| Exp.Sizeof (_, None, _) ->
| Exp.Sizeof {dynamic_length=None} ->
e
| Exp.Sizeof (typ, Some len, st) ->
Exp.Sizeof (typ, Some (exp_construct_fresh side len), st)
| Exp.Sizeof ({dynamic_length=Some len} as sizeof) ->
Exp.Sizeof {sizeof with dynamic_length=Some (exp_construct_fresh side len)}
let strexp_construct_fresh side =
let f (e, inst_opt) = (exp_construct_fresh side e, inst_opt) in
@ -971,9 +971,16 @@ let rec exp_partial_join (e1: Exp.t) (e2: Exp.t) : Exp.t =
let e1'' = exp_partial_join e1 e2 in
let e2'' = exp_partial_join e1' e2' in
Exp.Lindex(e1'', e2'')
| Exp.Sizeof (t1, len1, st1), Exp.Sizeof (t2, len2, st2) ->
Exp.Sizeof
(typ_partial_join t1 t2, dynamic_length_partial_join len1 len2, Subtype.join st1 st2)
| Exp.Sizeof {typ=t1; nbytes=nbytes1; dynamic_length=len1; subtype=st1},
Exp.Sizeof {typ=t2; nbytes=nbytes2; dynamic_length=len2; subtype=st2} ->
(* forget the static sizes if they differ *)
let nbytes_join i1 i2 = if Int.equal i1 i2 then Some i1 else None in
Exp.Sizeof {
typ=typ_partial_join t1 t2;
nbytes=option_partial_join nbytes_join nbytes1 nbytes2;
dynamic_length=dynamic_length_partial_join len1 len2;
subtype=Subtype.join st1 st2;
}
| _ ->
L.d_str "exp_partial_join no match "; Sil.d_exp e1; L.d_str " "; Sil.d_exp e2; L.d_ln ();
raise Sil.JoinFail

@ -298,11 +298,13 @@ let rec dotty_mk_node pe sigma =
let n = !dotty_state_count in
incr dotty_state_count;
let do_hpred_lambda exp_color = function
| (Sil.Hpointsto (e, Sil.Earray (e', l, _), Exp.Sizeof ({Typ.desc=Tarray (t, _)}, _, _)), lambda) ->
| (Sil.Hpointsto (e, Sil.Earray (e', l, _), Exp.Sizeof {typ={Typ.desc=Tarray (t, _)}}),
lambda) ->
incr dotty_state_count; (* increment once more n+1 is the box for the array *)
let e_color_str = color_to_str (exp_color e) in
let e_color_str'= color_to_str (exp_color e') in
[Dotpointsto((mk_coordinate n lambda), e, e_color_str); Dotarray((mk_coordinate (n + 1) lambda), e, e', l, t, e_color_str')]
[Dotpointsto((mk_coordinate n lambda), e, e_color_str);
Dotarray((mk_coordinate (n + 1) lambda), e, e', l, t, e_color_str')]
| (Sil.Hpointsto (e, Sil.Estruct (l, _), te), lambda) ->
incr dotty_state_count; (* increment once more n+1 is the box for the struct *)
let e_color_str = color_to_str (exp_color e) in
@ -692,8 +694,8 @@ let filter_useless_spec_dollar_box (nodes: dotty_node list) (links: link list) =
(* print a struct node *)
let rec print_struct f pe e te l coo c =
let print_type = match te with
| Exp.Sizeof (t, _, _) ->
let str_t = Typ.to_string t in
| Exp.Sizeof {typ} ->
let str_t = Typ.to_string typ in
(match Str.split_delim (Str.regexp_string Config.anonymous_block_prefix) str_t with
| [_; _] -> "BLOCK object"
| _ -> str_t)

@ -407,9 +407,9 @@ and _exp_rv_dexp tenv (_seen : Exp.Set.t) node e : DExp.t option =
| Exp.Cast (_, e1) ->
if verbose then (L.d_str "exp_rv_dexp: Cast "; Sil.d_exp e; L.d_ln ());
_exp_rv_dexp tenv seen node e1
| Exp.Sizeof (typ, len, sub) ->
| Exp.Sizeof {typ; dynamic_length; subtype} ->
if verbose then (L.d_str "exp_rv_dexp: type "; Sil.d_exp e; L.d_ln ());
Some (DExp.Dsizeof (typ, Option.bind len (_exp_rv_dexp tenv seen node), sub))
Some (DExp.Dsizeof (typ, Option.bind dynamic_length (_exp_rv_dexp tenv seen node), subtype))
| _ ->
if verbose then (L.d_str "exp_rv_dexp: no match for "; Sil.d_exp e; L.d_ln ());
None
@ -511,9 +511,9 @@ let explain_leak tenv hpred prop alloc_att_opt bucket =
(Pvar.is_local pvar || Pvar.is_global pvar) &&
not (Pvar.is_frontend_tmp pvar) &&
match hpred_typ_opt, find_typ_without_ptr prop pvar with
| Some (Exp.Sizeof (t1, _, _)), Some (Exp.Sizeof ({Typ.desc=Tptr (t2, _)}, _, _)) ->
| Some (Exp.Sizeof {typ=t1}), Some (Exp.Sizeof {typ={Typ.desc=Tptr (t2, _)}}) ->
Typ.equal t1 t2
| Some (Exp.Sizeof ({Typ.desc=Tint _}, _, _)), Some (Exp.Sizeof ({Typ.desc=Tint _}, _, _))
| Some (Exp.Sizeof {typ={Typ.desc=Tint _}}), Some (Exp.Sizeof {typ={Typ.desc=Tint _}})
when is_file -> (* must be a file opened with "open" *)
true
| _ -> false in
@ -581,7 +581,7 @@ let vpath_find tenv prop _exp : DExp.t option * Typ.t option =
(match lexp with
| Exp.Lvar pv ->
let typo = match texp with
| Exp.Sizeof ({Typ.desc=Tstruct name}, _, _) -> (
| Exp.Sizeof {typ={Typ.desc=Tstruct name}} -> (
match Tenv.lookup tenv name with
| Some {fields} ->
List.find ~f:(fun (f', _, _) -> Fieldname.equal f' f) fields |>
@ -607,7 +607,7 @@ let vpath_find tenv prop _exp : DExp.t option * Typ.t option =
(match lexp with
| Exp.Lvar pv when not (Pvar.is_frontend_tmp pv) ->
let typo = match texp with
| Exp.Sizeof (typ, _, _) -> Some typ
| Exp.Sizeof {typ} -> Some typ
| _ -> None in
Some (DExp.Dpvar pv), typo
| Exp.Var id ->

@ -621,13 +621,13 @@ let get_fld_typ_path_opt src_exps sink_exp_ reachable_hpreds_ =
| Sil.Eexp (e, _) -> Exp.equal target_exp e
| _ -> false in
let extend_path hpred (sink_exp, path, reachable_hpreds) = match hpred with
| Sil.Hpointsto (lhs, Sil.Estruct (flds, _), Exp.Sizeof (typ, _, _)) ->
| Sil.Hpointsto (lhs, Sil.Estruct (flds, _), Exp.Sizeof {typ}) ->
List.find ~f:(function _, se -> strexp_matches sink_exp se) flds |>
Option.value_map ~f:(function fld, _ ->
let reachable_hpreds' = Sil.HpredSet.remove hpred reachable_hpreds in
(lhs, (Some fld, typ) :: path, reachable_hpreds'))
~default:(sink_exp, path, reachable_hpreds)
| Sil.Hpointsto (lhs, Sil.Earray (_, elems, _), Exp.Sizeof (typ, _, _)) ->
| Sil.Hpointsto (lhs, Sil.Earray (_, elems, _), Exp.Sizeof {typ}) ->
if List.exists ~f:(function _, se -> strexp_matches sink_exp se) elems
then
let reachable_hpreds' = Sil.HpredSet.remove hpred reachable_hpreds in
@ -672,7 +672,7 @@ let report_context_leaks pname sigma tenv =
let context_exps =
List.fold
~f:(fun exps hpred -> match hpred with
| Sil.Hpointsto (_, Eexp (exp, _), Sizeof ({desc=Tptr ({desc=Tstruct name}, _)}, _, _))
| Sil.Hpointsto (_, Eexp (exp, _), Sizeof {typ={desc=Tptr ({desc=Tstruct name}, _)}})
when not (Exp.is_null_literal exp)
&& AndroidFramework.is_context tenv name
&& not (AndroidFramework.is_application tenv name) ->
@ -853,8 +853,10 @@ let prop_init_formals_seed tenv new_formals (prop : 'a Prop.t) : Prop.exposed Pr
let sigma_new_formals =
let do_formal (pv, typ) =
let texp = match !Config.curr_language with
| Config.Clang -> Exp.Sizeof (typ, None, Subtype.exact)
| Config.Java -> Exp.Sizeof (typ, None, Subtype.subtypes) in
| Config.Clang ->
Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact}
| Config.Java ->
Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.subtypes} in
Prop.mk_ptsto_lvar tenv Prop.Fld_init Sil.inst_formal (pv, texp, None) in
List.map ~f:do_formal new_formals in
let sigma_seed =

@ -529,7 +529,7 @@ let rec pi_sorted_remove_redundant (pi : pi) = match pi with
let sigma_get_unsigned_exps sigma =
let uexps = ref [] in
let do_hpred (hpred : Sil.hpred) = match hpred with
| Hpointsto (_, Eexp (e, _), Sizeof ({desc=Tint ik}, _, _))
| Hpointsto (_, Eexp (e, _), Sizeof {typ={desc=Tint ik}})
when Typ.ikind_is_unsigned ik ->
uexps := e :: !uexps
| _ -> () in
@ -712,10 +712,10 @@ module Normalize = struct
Closure { c with captured_vars; }
| Const _ ->
e
| Sizeof ({desc=Tarray ({desc=Tint ik}, _)}, Some l, _)
| Sizeof {typ={desc=Tarray ({desc=Tint ik}, _)}; dynamic_length=Some l}
when Typ.ikind_is_char ik && Config.curr_language_is Config.Clang ->
eval l
| Sizeof ({desc=Tarray ({desc=Tint ik}, Some l)}, _, _)
| Sizeof {typ={desc=Tarray ({desc=Tint ik}, Some l)}}
when Typ.ikind_is_char ik && Config.curr_language_is Config.Clang ->
Const (Cint l)
| Sizeof _ ->
@ -876,11 +876,11 @@ module Normalize = struct
match e1', e2' with
(* pattern for arrays and extensible structs:
sizeof(struct s {... t[l]}) + k * sizeof(t)) = sizeof(struct s {... t[l + k]}) *)
| Sizeof (typ, len1_opt, st),
BinOp (Mult, len2, Sizeof (elt, None, _))
| Sizeof ({typ; dynamic_length=len1_opt} as sizeof_data),
BinOp (Mult, len2, Sizeof {typ=elt; dynamic_length=None})
when isPlusA && (extensible_array_element_typ_equal elt typ) ->
let len = match len1_opt with Some len1 -> len1 +++ len2 | None -> len2 in
Sizeof (typ, Some len, st)
Sizeof {sizeof_data with dynamic_length=Some len}
| Const c, _ when Const.iszero_int_float c ->
e2'
| _, Const c when Const.iszero_int_float c ->
@ -991,11 +991,13 @@ module Normalize = struct
Exp.int (IntLit.div n m)
| Const (Cfloat v), Const (Cfloat w) ->
Exp.float (v /.w)
| Sizeof ({desc=Tarray (elt, _)}, Some len, _), Sizeof (elt2, None, _)
| Sizeof {typ={desc=Tarray (elt, _)}; dynamic_length=Some len},
Sizeof {typ=elt2; dynamic_length=None}
(* pattern: sizeof(elt[len]) / sizeof(elt) = len *)
when Typ.equal elt elt2 ->
len
| Sizeof ({desc=Tarray (elt, Some len)}, None, _), Sizeof (elt2, None, _)
| Sizeof {typ={desc=Tarray (elt, Some len)}; dynamic_length=None},
Sizeof {typ=elt2; dynamic_length=None}
(* pattern: sizeof(elt[len]) / sizeof(elt) = len *)
when Typ.equal elt elt2 ->
Const (Cint len)
@ -1101,8 +1103,9 @@ module Normalize = struct
else sym_eval tenv false exp'
let texp_normalize tenv sub (exp : Exp.t) : Exp.t = match exp with
| Sizeof (typ, len, st) ->
Sizeof (typ, Option.map ~f:(exp_normalize tenv sub) len, st)
| Sizeof ({dynamic_length} as sizeof_data) ->
Sizeof {sizeof_data with
dynamic_length=Option.map ~f:(exp_normalize tenv sub) dynamic_length}
| _ ->
exp_normalize tenv sub exp
@ -1348,8 +1351,8 @@ module Normalize = struct
initialize the fields of structs with fresh variables. *)
let mk_ptsto_exp tenv struct_init_mode (exp, (te : Exp.t), expo) inst : Sil.hpred =
let default_strexp () : Sil.strexp = match te with
| Sizeof (typ, len, _) ->
create_strexp_of_type tenv struct_init_mode typ len inst
| Sizeof {typ; dynamic_length} ->
create_strexp_of_type tenv struct_init_mode typ dynamic_length inst
| Var _ ->
Estruct ([], inst)
| te ->
@ -1372,30 +1375,37 @@ module Normalize = struct
let normalized_cnt = strexp_normalize tenv sub cnt in
let normalized_te = texp_normalize tenv sub te in
begin match normalized_cnt, normalized_te with
| Earray (Exp.Sizeof _ as size, [], inst), Sizeof ({desc=Tarray _}, _, _) ->
| Earray (Exp.Sizeof _ as size, [], inst), Sizeof {typ={desc=Tarray _}} ->
(* check for an empty array whose size expression is (Sizeof type), and turn the array
into a strexp of the given type *)
let hpred' = mk_ptsto_exp tenv Fld_init (root, size, None) inst in
replace_hpred hpred'
| Earray (BinOp (Mult, Sizeof (t, None, st1), x), esel, inst),
Sizeof ({desc=Tarray (elt, _)} as arr, _, _) when Typ.equal t elt ->
let len = Some x in
let hpred' = mk_ptsto_exp tenv Fld_init (root, Sizeof (arr, len, st1), None) inst in
| Earray (BinOp (Mult, Sizeof {typ=t; dynamic_length=None; subtype=st1}, x), esel, inst),
Sizeof {typ={desc=Tarray (elt, _)} as arr} when Typ.equal t elt ->
let dynamic_length = Some x in
let sizeof_data = {Exp.typ=arr; nbytes=None; dynamic_length; subtype=st1} in
let hpred' =
mk_ptsto_exp tenv Fld_init (root, Sizeof sizeof_data, None) inst in
replace_hpred (replace_array_contents hpred' esel)
| Earray (BinOp (Mult, x, Sizeof (t, None, st1)), esel, inst),
Sizeof ({desc=Tarray (elt, _)} as arr, _, _) when Typ.equal t elt ->
let len = Some x in
let hpred' = mk_ptsto_exp tenv Fld_init (root, Sizeof (arr, len, st1), None) inst in
| Earray (BinOp (Mult, x, Sizeof {typ; dynamic_length=None; subtype}), esel, inst),
Sizeof {typ={desc=Tarray (elt, _)} as arr} when Typ.equal typ elt ->
let sizeof_data = {Exp.typ=arr; nbytes=None; dynamic_length=Some x; subtype} in
let hpred' =
mk_ptsto_exp tenv Fld_init (root, Sizeof sizeof_data, None) inst in
replace_hpred (replace_array_contents hpred' esel)
| Earray (BinOp (Mult, Sizeof (t, Some len, st1), x), esel, inst),
Sizeof ({desc=Tarray (elt, _)} as arr, _, _) when Typ.equal t elt ->
let len = Some (Exp.BinOp(Mult, x, len)) in
let hpred' = mk_ptsto_exp tenv Fld_init (root, Sizeof (arr, len, st1), None) inst in
| Earray (BinOp (Mult, Sizeof {typ; dynamic_length=Some len; subtype}, x), esel, inst),
Sizeof {typ={desc=Tarray (elt, _)} as arr} when Typ.equal typ elt ->
let sizeof_data = {Exp.typ=arr; nbytes=None;
dynamic_length=Some (Exp.BinOp(Mult, x, len)); subtype} in
let hpred' =
mk_ptsto_exp tenv Fld_init (root, Sizeof sizeof_data, None) inst in
replace_hpred (replace_array_contents hpred' esel)
| Earray (BinOp (Mult, x, Sizeof (t, Some len, st1)), esel, inst),
Sizeof ({desc=Tarray (elt, _)} as arr, _, _) when Typ.equal t elt ->
let len = Some (Exp.BinOp(Mult, x, len)) in
let hpred' = mk_ptsto_exp tenv Fld_init (root, Sizeof (arr, len, st1), None) inst in
| Earray (BinOp (Mult, x, Sizeof {typ; dynamic_length=Some len; subtype}), esel, inst),
Sizeof {typ={desc=Tarray (elt, _)} as arr} when Typ.equal typ elt ->
let sizeof_data = {Exp.typ=arr; nbytes=None;
dynamic_length=Some (Exp.BinOp(Mult, x, len)); subtype} in
let hpred' =
mk_ptsto_exp tenv Fld_init (root, Sizeof sizeof_data, None) inst in
replace_hpred (replace_array_contents hpred' esel)
| _ ->
Hpointsto (normalized_root, normalized_cnt, normalized_te)
@ -1986,8 +1996,8 @@ let rec exp_captured_ren ren (e : Exp.t) : Exp.t = match e with
e (* TODO: why captured vars not renamed? *)
| Const _ ->
e
| Sizeof (t, len, st) ->
Sizeof (t, Option.map ~f:(exp_captured_ren ren) len, st)
| Sizeof ({dynamic_length} as sizeof_data) ->
Sizeof {sizeof_data with dynamic_length=Option.map ~f:(exp_captured_ren ren) dynamic_length}
| Cast (t, e) ->
Cast (t, exp_captured_ren ren e)
| UnOp (op, e, topt) ->

@ -383,7 +383,7 @@ end = struct
| Some {Typ.desc=Tint ik} -> Typ.ikind_is_unsigned ik
| _ -> false in
let type_of_texp = function
| Exp.Sizeof (t, _, _) -> Some t
| Exp.Sizeof {typ} -> Some typ
| _ -> None in
let texp_is_unsigned texp = type_opt_is_unsigned @@ type_of_texp texp in
let strexp_lt_minus1 = function
@ -436,7 +436,9 @@ end = struct
(* L.d_str "check_le "; Sil.d_exp e1; L.d_str " "; Sil.d_exp e2; L.d_ln (); *)
match e1, e2 with
| Exp.Const (Const.Cint n1), Exp.Const (Const.Cint n2) -> IntLit.leq n1 n2
| Exp.BinOp (Binop.MinusA, Exp.Sizeof (t1, None, _), Exp.Sizeof (t2, None, _)),
| Exp.BinOp (Binop.MinusA,
Exp.Sizeof {typ=t1; dynamic_length=None},
Exp.Sizeof {typ=t2; dynamic_length=None}),
Exp.Const(Const.Cint n2)
when IntLit.isminusone n2 && type_size_comparable t1 t2 ->
(* [ sizeof(t1) - sizeof(t2) <= -1 ] *)
@ -1220,9 +1222,11 @@ let exp_imply tenv calc_missing subs e1_in e2_in : subst2 =
do_imply subs (Exp.Var v1) (Exp.BinOp (Binop.MinusA, e2, e1))
| Exp.BinOp (Binop.PlusPI, Exp.Lvar pv1, e1), e2 ->
do_imply subs (Exp.Lvar pv1) (Exp.BinOp (Binop.MinusA, e2, e1))
| Exp.Sizeof (t1, None, st1), Exp.Sizeof (t2, None, st2)
| Exp.Sizeof {typ=t1; dynamic_length=None; subtype=st1},
Exp.Sizeof {typ=t2; dynamic_length=None; subtype=st2}
when Typ.equal t1 t2 && Subtype.equal_modulo_flag st1 st2 -> subs
| Exp.Sizeof (t1, Some d1, st1), Exp.Sizeof (t2, Some d2, st2)
| Exp.Sizeof {typ=t1; dynamic_length=Some d1; subtype=st1},
Exp.Sizeof {typ=t2; dynamic_length=Some d2; subtype=st2}
when Typ.equal t1 t2 && Exp.equal d1 d2 && Subtype.equal_modulo_flag st1 st2 -> subs
| e', Exp.Const (Const.Cint n)
when IntLit.iszero n && check_disequal tenv Prop.prop_emp e' Exp.zero ->
@ -1504,7 +1508,8 @@ let expand_hpred_pointer =
match Tenv.lookup tenv name with
| Some _ ->
(* type of struct at adr_base is known *)
Some (Exp.Sizeof (adr_typ, None, Subtype.exact))
Some (Exp.Sizeof {typ=adr_typ; nbytes=None;
dynamic_length=None; subtype=Subtype.exact})
| None -> None
)
| _ -> None
@ -1512,14 +1517,14 @@ let expand_hpred_pointer =
| Some se -> se
| None ->
match cnt_texp with
| Sizeof (cnt_typ, len, st) ->
| Sizeof ({typ=cnt_typ} as sizeof_data) ->
(* type of struct at adr_base is unknown (typically Tvoid), but
type of contents is known, so construct struct type for single fld:cnt_typ *)
let name = Typ.Name.C.from_string ("counterfeit" ^ string_of_int !count) in
incr count ;
let fields = [(fld, cnt_typ, Annot.Item.empty)] in
ignore (Tenv.mk_struct tenv ~fields name) ;
Exp.Sizeof (Typ.mk (Tstruct name), len, st)
Exp.Sizeof {sizeof_data with typ=Typ.mk (Tstruct name)}
| _ ->
(* type of struct at adr_base and of contents are both unknown: give up *)
raise (Failure "expand_hpred_pointer: Unexpected non-sizeof type in Lfield") in
@ -1527,10 +1532,11 @@ let expand_hpred_pointer =
expand true true hpred'
| Sil.Hpointsto (Exp.Lindex (e, ind), se, t) ->
let t' = match t with
| Exp.Sizeof (t_, len, st) -> Exp.Sizeof (Typ.mk (Tarray (t_, None)), len, st)
| Exp.Sizeof ({typ=t_} as sizeof_data) ->
Exp.Sizeof {sizeof_data with typ=Typ.mk (Tarray (t_, None))}
| _ -> raise (Failure "expand_hpred_pointer: Unexpected non-sizeof type in Lindex") in
let len = match t' with
| Exp.Sizeof (_, Some len, _) -> len
| Exp.Sizeof {dynamic_length=Some len} -> len
| _ -> Exp.get_undefined false in
let hpred' = Sil.Hpointsto (e, Sil.Earray (len, [(ind, se)], Sil.inst_none), t') in
expand true true hpred'
@ -1611,26 +1617,27 @@ struct
case, if they are possible *)
let subtype_case_analysis tenv texp1 texp2 =
match texp1, texp2 with
| Exp.Sizeof (t1, len1, st1), Exp.Sizeof (t2, len2, st2) ->
begin
let pos_opt, neg_opt = case_analysis_type tenv (t1, st1) (t2, st2) in
let pos_type_opt = match pos_opt with
| None -> None
| Some st1' ->
let t1', len1' = if check_subtype tenv t1 t2 then t1, len1 else t2, len2 in
Some (Exp.Sizeof (t1', len1', st1')) in
let neg_type_opt = match neg_opt with
| None -> None
| Some st1' -> Some (Exp.Sizeof (t1, len1, st1')) in
pos_type_opt, neg_type_opt
end
| Exp.Sizeof sizeof1, Exp.Sizeof sizeof2 ->
let pos_opt, neg_opt = case_analysis_type tenv
(sizeof1.typ, sizeof1.subtype) (sizeof2.typ, sizeof2.subtype) in
let pos_type_opt = match pos_opt with
| None -> None
| Some subtype ->
if check_subtype tenv sizeof1.typ sizeof2.typ then
Some (Exp.Sizeof {sizeof1 with subtype})
else
Some (Exp.Sizeof {sizeof2 with subtype}) in
let neg_type_opt = match neg_opt with
| None -> None
| Some subtype -> Some (Exp.Sizeof {sizeof1 with subtype}) in
pos_type_opt, neg_type_opt
| _ -> (* don't know, consider both possibilities *)
Some texp1, Some texp1
end
let cast_exception tenv texp1 texp2 e1 subs =
let _ = match texp1, texp2 with
| Exp.Sizeof (t1, _, _), Exp.Sizeof (t2, _, st2) ->
| Exp.Sizeof {typ=t1}, Exp.Sizeof {typ=t2; subtype=st2} ->
if Config.developer_mode ||
(Subtype.is_cast st2 &&
not (Subtyping_check.check_subtype tenv t1 t2)) then
@ -1666,7 +1673,8 @@ let get_overrides_of tenv supertype pname =
(** Check the equality of two types ignoring flags in the subtyping components *)
let texp_equal_modulo_subtype_flag texp1 texp2 = match texp1, texp2 with
| Exp.Sizeof (t1, len1, st1), Exp.Sizeof (t2, len2, st2) ->
| Exp.Sizeof {typ=t1; dynamic_length=len1; subtype=st1},
Exp.Sizeof {typ=t2; dynamic_length=len2; subtype=st2} ->
[%compare.equal: Typ.t * Exp.t option] (t1, len1) (t2, len2)
&& Subtype.equal_modulo_flag st1 st2
| _ -> Exp.equal texp1 texp2
@ -1677,7 +1685,7 @@ let texp_imply tenv subs texp1 texp2 e1 calc_missing =
(* classes and arrays in Java, and just classes in C++ and ObjC *)
let types_subject_to_dynamic_cast =
match texp1, texp2 with
| Exp.Sizeof (typ1, _, _), Exp.Sizeof (typ2, _, _) -> (
| Exp.Sizeof {typ=typ1}, Exp.Sizeof {typ=typ2} -> (
match typ1.desc, typ2.desc with
| (Tstruct _ | Tarray _), (Tstruct _ | Tarray _) ->
is_java_class tenv typ1
@ -1737,24 +1745,26 @@ let handle_parameter_subtype tenv prop1 sigma2 subs (e1, se1, texp1) (se2, texp2
let type_rhs e =
let sub_opt = ref None in
let filter = function
| Sil.Hpointsto(e', _, Exp.Sizeof(t, len, sub)) when Exp.equal e' e ->
sub_opt := Some (t, len, sub);
| Sil.Hpointsto(e', _, Exp.Sizeof sizeof_data) when Exp.equal e' e ->
sub_opt := Some sizeof_data;
true
| _ -> false in
if List.exists ~f:filter sigma2 then !sub_opt else None in
let add_subtype () = match texp1, texp2, se1, se2 with
| Exp.Sizeof ({desc=Tptr (t1, _)}, None, _), Exp.Sizeof ({desc=Tptr (t2, _)}, None, _),
| Exp.Sizeof {typ={desc=Tptr (t1, _)}; dynamic_length=None},
Exp.Sizeof {typ={desc=Tptr (t2, _)}; dynamic_length=None},
Sil.Eexp (e1', _), Sil.Eexp (e2', _)
when not (is_allocated_lhs e1') ->
begin
match type_rhs e2' with
| Some (t2_ptsto, len2, sub2) ->
| Some sizeof_data2 ->
if not (Typ.equal t1 t2) && Subtyping_check.check_subtype tenv t1 t2
then begin
let pos_type_opt, _ =
Subtyping_check.subtype_case_analysis tenv
(Exp.Sizeof (t1, None, Subtype.subtypes))
(Exp.Sizeof (t2_ptsto, len2, sub2)) in
(Exp.Sizeof {typ=t1; nbytes=None;
dynamic_length=None; subtype=Subtype.subtypes})
(Exp.Sizeof sizeof_data2) in
match pos_type_opt with
| Some t1_noptr ->
ProverState.add_frame_typ (e1', t1_noptr);
@ -1984,15 +1994,18 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : (subst2 *
let fld = Fieldname.Java.from_string s in
let se = Sil.Eexp (Exp.Var (Ident.create_fresh Ident.kprimed), Sil.Inone) in
(fld, se) in
let fields = ["java.lang.String.count"; "java.lang.String.hash"; "java.lang.String.offset"; "java.lang.String.value"] in
let fields = ["java.lang.String.count"; "java.lang.String.hash";
"java.lang.String.offset"; "java.lang.String.value"] in
Sil.Estruct (List.map ~f:mk_fld_sexp fields, Sil.inst_none) in
let const_string_texp =
match !Config.curr_language with
| Config.Clang ->
Exp.Sizeof (Typ.mk (Tarray (Typ.mk (Tint Typ.IChar), Some len)), None, Subtype.exact)
Exp.Sizeof {typ=Typ.mk (Tarray (Typ.mk (Tint Typ.IChar), Some len));
nbytes=None; dynamic_length=None; subtype=Subtype.exact}
| Config.Java ->
let object_type = Typ.Name.Java.from_string "java.lang.String" in
Exp.Sizeof (Typ.mk (Tstruct object_type), None, Subtype.exact) in
Exp.Sizeof {typ=Typ.mk (Tstruct object_type);
nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
Sil.Hpointsto (root, sexp, const_string_texp) in
let mk_constant_class_hpred s = (* creat an hpred from a constant class *)
let root = Exp.Const (Const.Cclass (Ident.string_to_name s)) in
@ -2002,7 +2015,8 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : (subst2 *
Sil.Eexp ((Exp.Const (Const.Cstr s), Sil.Inone)))], Sil.inst_none) in
let class_texp =
let class_type = Typ.Name.Java.from_string "java.lang.Class" in
Exp.Sizeof (Typ.mk (Tstruct class_type), None, Subtype.exact) in
Exp.Sizeof {typ=Typ.mk (Tstruct class_type);
nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
Sil.Hpointsto (root, sexp, class_texp) in
try
(match move_primed_lhs_from_front subs sigma2 with

@ -408,10 +408,11 @@ let strexp_extend_values
let check_not_inconsistent (atoms, _, _) = not (List.exists ~f:check_neg_atom atoms) in
List.filter ~f:check_not_inconsistent atoms_se_typ_list in
if Config.trace_rearrange then L.d_strln "exiting strexp_extend_values";
let len, st = match te with
| Exp.Sizeof(_, len, st) -> (len, st)
| _ -> None, Subtype.exact in
List.map ~f:(fun (atoms', se', typ') -> (laundry_atoms @ atoms', se', Exp.Sizeof (typ', len, st)))
let sizeof_data = match te with
| Exp.Sizeof sizeof_data -> sizeof_data
| _ -> {Exp.typ=Typ.mk Typ.Tvoid; nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
List.map ~f:(fun (atoms', se', typ') ->
(laundry_atoms @ atoms', se', Exp.Sizeof { sizeof_data with typ=typ'}))
atoms_se_typ_list_filtered
let collect_root_offset exp =
@ -440,24 +441,27 @@ let mk_ptsto_exp_footprint
end
end;
let off_foot, eqs = laundry_offset_for_footprint max_stamp off in
let st = match !Config.curr_language with
let subtype = match !Config.curr_language with
| Config.Clang -> Subtype.exact
| Config.Java -> Subtype.subtypes in
let create_ptsto footprint_part off0 = match root, off0, typ.Typ.desc with
| Exp.Lvar pvar, [], Typ.Tfun _ ->
let fun_name = Typ.Procname.from_string_c_fun (Mangled.to_string (Pvar.get_name pvar)) in
let fun_exp = Exp.Const (Const.Cfun fun_name) in
([], Prop.mk_ptsto tenv root (Sil.Eexp (fun_exp, inst)) (Exp.Sizeof (typ, None, st)))
([], Prop.mk_ptsto tenv root (Sil.Eexp (fun_exp, inst))
(Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype}))
| _, [], Typ.Tfun _ ->
let atoms, se, t =
let atoms, se, typ =
create_struct_values
pname tenv orig_prop footprint_part Ident.kfootprint max_stamp typ off0 inst in
(atoms, Prop.mk_ptsto tenv root se (Exp.Sizeof (t, None, st)))
(atoms, Prop.mk_ptsto tenv root se
(Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype}))
| _ ->
let atoms, se, t =
let atoms, se, typ =
create_struct_values
pname tenv orig_prop footprint_part Ident.kfootprint max_stamp typ off0 inst in
(atoms, Prop.mk_ptsto tenv root se (Exp.Sizeof (t, None, st))) in
(atoms, Prop.mk_ptsto tenv root se
(Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype})) 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
@ -748,7 +752,8 @@ let add_guarded_by_constraints tenv prop lexp pdesc =
| Some (Sil.Eexp (matching_exp, _), _) ->
List.find_map
~f:(function
| Sil.Hpointsto (lhs_exp, Estruct (matching_flds, _), Sizeof (fld_typ, _, _))
| Sil.Hpointsto (lhs_exp, Estruct (matching_flds, _),
Sizeof {typ=fld_typ})
when Exp.equal lhs_exp matching_exp ->
get_fld_strexp_and_typ
fld_typ (is_guarded_by_fld field_part) matching_flds
@ -761,37 +766,38 @@ let add_guarded_by_constraints tenv prop lexp pdesc =
| _ ->
None in
List.find_map
~f:(function [@warning "-57"] (* FIXME: silenced warning may be legit *)
| Sil.Hpointsto ((Const (Cclass clazz) as lhs_exp), _, Exp.Sizeof (typ, _, _))
| Sil.Hpointsto (_, Sil.Eexp (Const (Cclass clazz) as lhs_exp, _), Exp.Sizeof (typ, _, _))
List.find_map ~f:(fun hpred -> (match hpred with
| Sil.Hpointsto ((Const (Cclass clazz) as lhs_exp), _,
Exp.Sizeof {typ})
| Sil.Hpointsto (_, Sil.Eexp (Const (Cclass clazz) as lhs_exp, _),
Exp.Sizeof {typ})
when guarded_by_str_is_class guarded_by_str0 (Ident.name_to_string clazz) ->
Some (Sil.Eexp (lhs_exp, Sil.inst_none), typ)
| Sil.Hpointsto (_, Estruct (flds, _), Exp.Sizeof (typ, _, _)) ->
begin
(* first, try to find a field that exactly matches the guarded-by string *)
match get_fld_strexp_and_typ typ (is_guarded_by_fld guarded_by_str0) flds with
| None when guarded_by_str_is_this guarded_by_str0 ->
(* if the guarded-by string is "OuterClass.this", look for "this$n" for some n.
note that this is a bit sketchy when there are mutliple this$n's, but there's
nothing we can do to disambiguate them. *)
get_fld_strexp_and_typ
typ
(fun f _ -> Fieldname.java_is_outer_instance f)
flds
| None ->
(* can't find an exact match. try a different convention. *)
match_on_field_type typ flds
| Some _ as res_opt ->
res_opt
end
| Sil.Hpointsto (Lvar pvar, rhs_exp, Exp.Sizeof (typ, _, _))
when (guarded_by_str_is_current_class_this guarded_by_str0 pname ||
guarded_by_str_is_super_class_this guarded_by_str0 pname
) && Pvar.is_this pvar ->
Some (rhs_exp, typ)
| _ ->
None)
| Sil.Hpointsto (_, Estruct (flds, _), Exp.Sizeof {typ}) ->
begin
(* first, try to find a field that exactly matches the guarded-by string *)
match get_fld_strexp_and_typ typ (is_guarded_by_fld guarded_by_str0) flds with
| None when guarded_by_str_is_this guarded_by_str0 ->
(* if the guarded-by string is "OuterClass.this", look for "this$n" for some n.
note that this is a bit sketchy when there are mutliple this$n's, but there's
nothing we can do to disambiguate them. *)
get_fld_strexp_and_typ
typ
(fun f _ -> Fieldname.java_is_outer_instance f)
flds
| None ->
(* can't find an exact match. try a different convention. *)
match_on_field_type typ flds
| Some _ as res_opt ->
res_opt
end
| Sil.Hpointsto (Lvar pvar, rhs_exp, Exp.Sizeof {typ})
when (guarded_by_str_is_current_class_this guarded_by_str0 pname ||
guarded_by_str_is_super_class_this guarded_by_str0 pname
) && Pvar.is_this pvar ->
Some (rhs_exp, typ)
| _ ->
None) [@warning "-57"] (* FIXME: silenced warning may be legit *))
sigma in
(* warn if the access to [lexp] is not protected by the [guarded_by_fld_str] lock *)
let enforce_guarded_access_ accessed_fld guarded_by_str prop =
@ -910,7 +916,7 @@ let add_guarded_by_constraints tenv prop lexp pdesc =
| Sil.Eexp (exp, _) when Exp.equal exp lexp -> enforce_guarded_access fld typ prop_acc
| _ -> prop_acc in
let hpred_check_flds prop_acc = function
| Sil.Hpointsto (_, Estruct (flds, _), Sizeof (typ, _, _)) ->
| Sil.Hpointsto (_, Estruct (flds, _), Sizeof {typ}) ->
List.fold ~f:(check_fld_locks typ) ~init:prop_acc flds
| _ ->
prop_acc in
@ -1144,8 +1150,7 @@ let type_at_offset tenv texp off =
strip_offset off' typ'
| _ -> None in
match texp with
| Exp.Sizeof(typ, _, _) ->
strip_offset off typ
| Exp.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
@ -1339,7 +1344,7 @@ let is_only_pt_by_fld_or_param_with_annot
if Option.is_some procname_str_opt then obj_str := procname_str_opt;
(* it's ok for a local with no annotation to point to deref_exp *)
var_has_annotation || Option.is_some procname_str_opt || Pvar.is_local pvar
| Sil.Hpointsto (_, Sil.Estruct (flds, _), Exp.Sizeof (typ, _, _)) ->
| Sil.Hpointsto (_, Sil.Estruct (flds, _), Exp.Sizeof {typ}) ->
List.for_all ~f:(is_strexp_pt_fld_with_annot tenv obj_str is_annotation typ deref_exp) flds
| _ -> true in
if List.for_all ~f:is_pt_by_fld_or_param_with_annot prop.Prop.sigma && !obj_str <> None

@ -209,7 +209,7 @@ let rec apply_offlist
Finally, before running this function, the tool should run strexp_extend_value
in rearrange.ml for the same se and offlist, so that all the necessary
extensions of se are done before this function. *)
let ptsto_lookup pdesc tenv p (lexp, se, typ, len, st) offlist id =
let ptsto_lookup pdesc tenv p (lexp, se, sizeof) offlist id =
let f =
function Some exp -> exp | None -> Exp.Var id in
let fp_root =
@ -217,12 +217,12 @@ let ptsto_lookup pdesc tenv p (lexp, se, typ, len, st) offlist id =
let lookup_inst = ref None in
let e', se', typ', pred_insts_op' =
apply_offlist
pdesc tenv p fp_root false (lexp, se, typ) offlist f Sil.inst_lookup lookup_inst in
pdesc tenv p fp_root false (lexp, se, sizeof.Exp.typ) offlist f Sil.inst_lookup lookup_inst in
let lookup_uninitialized = (* true if we have looked up an uninitialized value *)
match !lookup_inst with
| Some (Sil.Iinitial | Sil.Ialloc | Sil.Ilookup) -> true
| _ -> false in
let ptsto' = Prop.mk_ptsto tenv lexp se' (Exp.Sizeof (typ', len, st)) in
let ptsto' = Prop.mk_ptsto tenv lexp se' (Exp.Sizeof {sizeof with typ=typ'}) in
(e', ptsto', pred_insts_op', lookup_uninitialized)
(** [ptsto_update p (lexp,se,typ) offlist exp] takes
@ -236,7 +236,7 @@ let ptsto_lookup pdesc tenv p (lexp, se, typ, len, st) offlist id =
the tool should run strexp_extend_value in rearrange.ml for the same
se and offlist, so that all the necessary extensions of se are done
before this function. *)
let ptsto_update pdesc tenv p (lexp, se, typ, len, st) offlist exp =
let ptsto_update pdesc tenv p (lexp, se, sizeof) offlist exp =
let f _ = exp in
let fp_root =
match lexp with Exp.Var id -> Ident.is_footprint id | _ -> false in
@ -244,8 +244,9 @@ let ptsto_update pdesc tenv p (lexp, se, typ, len, st) offlist exp =
let _, se', typ', pred_insts_op' =
let pos = State.get_path_pos () in
apply_offlist
pdesc tenv p fp_root true (lexp, se, typ) offlist f (State.get_inst_update pos) lookup_inst in
let ptsto' = Prop.mk_ptsto tenv lexp se' (Exp.Sizeof (typ', len, st)) in
pdesc tenv p fp_root true (lexp, se, sizeof.Exp.typ) offlist f
(State.get_inst_update pos) lookup_inst in
let ptsto' = Prop.mk_ptsto tenv lexp se' (Exp.Sizeof {sizeof with typ=typ'}) in
(ptsto', pred_insts_op')
let update_iter iter pi sigma =
@ -520,7 +521,7 @@ let resolve_typename prop receiver_exp =
| _ :: hpreds -> loop hpreds in
loop prop.Prop.sigma in
match typexp_opt with
| Some (Exp.Sizeof ({desc=Tstruct name}, _, _)) -> Some name
| Some (Exp.Sizeof {typ={desc=Tstruct name}}) -> Some name
| _ -> None
(** If the dynamic type of the receiver actual T_actual is a subtype of the reciever type T_formal
@ -809,7 +810,7 @@ let do_error_checks tenv node_opt instr pname pdesc = match node_opt with
let add_strexp_to_footprint tenv strexp abduced_pv typ prop =
let abduced_lvar = Exp.Lvar abduced_pv in
let lvar_pt_fpvar =
let sizeof_exp = Exp.Sizeof (typ, None, Subtype.subtypes) in
let sizeof_exp = Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.subtypes} in
Prop.mk_ptsto tenv abduced_lvar strexp sizeof_exp in
let sigma_fp = prop.Prop.sigma_fp in
Prop.normalize tenv (Prop.set prop ~sigma_fp:(lvar_pt_fpvar :: sigma_fp))
@ -905,9 +906,9 @@ let execute_load ?(report_deref_errors=true) pname pdesc tenv id rhs_exp typ loc
let iter_ren = Prop.prop_iter_make_id_primed tenv id iter in
let prop_ren = Prop.prop_iter_to_prop tenv iter_ren in
match Prop.prop_iter_current tenv iter_ren with
| (Sil.Hpointsto(lexp, strexp, Exp.Sizeof (typ, len, st)), offlist) ->
| (Sil.Hpointsto(lexp, strexp, Exp.Sizeof sizeof_data), offlist) ->
let contents, new_ptsto, pred_insts_op, lookup_uninitialized =
ptsto_lookup pdesc tenv prop_ren (lexp, strexp, typ, len, st) offlist id in
ptsto_lookup pdesc tenv prop_ren (lexp, strexp, sizeof_data) offlist id in
let update acc (pi, sigma) =
let pi' = Sil.Aeq (Exp.Var(id), contents):: pi in
let sigma' = new_ptsto:: sigma in
@ -978,14 +979,14 @@ let load_ret_annots pname =
let execute_store ?(report_deref_errors=true) pname pdesc tenv lhs_exp typ rhs_exp loc prop_ =
let execute_store_ pdesc tenv rhs_exp acc_in iter =
let (lexp, strexp, typ, len, st, offlist) =
let (lexp, strexp, sizeof, offlist) =
match Prop.prop_iter_current tenv iter with
| (Sil.Hpointsto(lexp, strexp, Exp.Sizeof (typ, len, st)), offlist) ->
(lexp, strexp, typ, len, st, offlist)
| (Sil.Hpointsto(lexp, strexp, Exp.Sizeof sizeof), offlist) ->
(lexp, strexp, sizeof, offlist)
| _ -> assert false in
let p = Prop.prop_iter_to_prop tenv iter in
let new_ptsto, pred_insts_op =
ptsto_update pdesc tenv p (lexp, strexp, typ, len, st) offlist rhs_exp in
ptsto_update pdesc tenv p (lexp, strexp, sizeof) offlist rhs_exp in
let update acc (pi, sigma) =
let sigma' = new_ptsto:: sigma in
let iter' = update_iter iter pi sigma' in
@ -1250,7 +1251,8 @@ let rec sym_exec tenv current_pdesc _instr (prop_: Prop.normal Prop.t) path
ret_old_path [Prop.exist_quantify tenv (Sil.fav_from_list temps) prop_]
| Sil.Declare_locals (ptl, _) ->
let sigma_locals =
let add_None (x, y) = (x, Exp.Sizeof (y, None, Subtype.exact), None) in
let add_None (x, typ) =
(x, Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact}, None) in
let sigma_locals () =
List.map
~f:(Prop.mk_ptsto_lvar tenv Prop.Fld_init Sil.inst_initial)
@ -1357,7 +1359,8 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call
let havoc_actual_by_ref prop (actual, actual_typ, _) =
let actual_pt_havocd_var =
let havocd_var = Exp.Var (Ident.create_fresh Ident.kprimed) in
let sizeof_exp = Exp.Sizeof (Typ.strip_ptr actual_typ, None, Subtype.subtypes) in
let sizeof_exp = Exp.Sizeof {typ=Typ.strip_ptr actual_typ; nbytes=None;
dynamic_length=None; subtype=Subtype.subtypes} in
Prop.mk_ptsto tenv actual (Sil.Eexp (havocd_var, Sil.Inone)) sizeof_exp in
replace_actual_hpred actual actual_pt_havocd_var prop in
let do_actual_by_ref =

@ -482,8 +482,8 @@ let texp_star tenv texp1 texp2 =
| _ ->
t1 in
match texp1, texp2 with
| Exp.Sizeof (t1, len1, st1), Exp.Sizeof (t2, _, st2) ->
Exp.Sizeof (typ_star t1 t2, len1, Subtype.join st1 st2)
| Exp.Sizeof ({typ=t1; subtype=st1} as sizeof1), Exp.Sizeof {typ=t2; subtype=st2} ->
Exp.Sizeof {sizeof1 with typ=typ_star t1 t2; subtype=Subtype.join st1 st2}
| _ ->
texp1
@ -634,7 +634,7 @@ let prop_get_exn_name pname prop =
let ret_pvar = Exp.Lvar (Pvar.get_ret_pvar pname) in
let rec search_exn e = function
| [] -> None
| Sil.Hpointsto (e1, _, Sizeof ({desc=Tstruct name}, _, _)) :: _
| Sil.Hpointsto (e1, _, Sizeof {typ={desc=Tstruct name}}) :: _
when Exp.equal e1 e ->
Some name
| _ :: tl -> search_exn e tl in
@ -882,7 +882,7 @@ let mk_actual_precondition tenv prop actual_params formal_params =
Prop.mk_ptsto tenv
(Exp.Lvar formal_var)
(Sil.Eexp (actual_e, Sil.inst_actual_precondition))
(Exp.Sizeof (actual_t, None, Subtype.exact)) in
(Exp.Sizeof {typ=actual_t; nbytes=None; dynamic_length=None; subtype=Subtype.exact}) in
let instantiated_formals = List.map ~f:mk_instantiation formals_actuals in
let actual_pre = Prop.prop_sigma_star prop instantiated_formals in
Prop.normalize tenv actual_pre

@ -36,12 +36,12 @@ struct
type extras = Typ.Procname.t -> Procdesc.t option
(* NOTE: heuristic *)
let get_malloc_info : Exp.t -> Typ.t * Exp.t
let get_malloc_info : Exp.t -> Typ.t * Int.t option * Exp.t
= function
| Exp.BinOp (Binop.Mult, Exp.Sizeof (typ, _, _), size)
| Exp.BinOp (Binop.Mult, size, Exp.Sizeof (typ, _, _)) -> (typ, size)
| Exp.Sizeof (typ, _, _) -> (typ, Exp.one)
| x -> (Typ.mk (Tint Typ.IChar), x)
| Exp.BinOp (Binop.Mult, Exp.Sizeof {typ; nbytes}, length)
| Exp.BinOp (Binop.Mult, length, Exp.Sizeof {typ; nbytes}) -> (typ, nbytes, length)
| Exp.Sizeof {typ; nbytes} -> (typ, nbytes, Exp.one)
| x -> (Typ.mk (Typ.Tint Typ.IChar), Some 1, x)
let model_malloc
: Typ.Procname.t -> (Ident.t * Typ.t) option -> (Exp.t * Typ.t) list -> CFG.node
@ -56,9 +56,9 @@ struct
Dom.Mem.weak_update_heap loc Dom.Val.top_itv mem
| _ -> mem
in
let (typ, size) = get_malloc_info (List.hd_exn params |> fst) in
let size = Sem.eval size mem (CFG.loc node) |> Dom.Val.get_itv in
let v = Sem.eval_array_alloc pname node typ Itv.zero size 0 1 in
let (typ, stride, length0) = get_malloc_info (List.hd_exn params |> fst) in
let length = Sem.eval length0 mem (CFG.loc node) |> Dom.Val.get_itv in
let v = Sem.eval_array_alloc pname node typ ?stride Itv.zero length 0 1 in
mem
|> Dom.Mem.add_stack (Loc.of_id id) v
|> set_uninitialized typ (Dom.Val.get_array_locs v)

@ -76,15 +76,18 @@ struct
must_alias e1 e2 m && Fieldname.equal fld1 fld2
| Exp.Lindex (e11, e12), Exp.Lindex (e21, e22) ->
must_alias e11 e21 m && must_alias e12 e22 m
| Exp.Sizeof (t1, dynlen1, subt1), Exp.Sizeof (t2, dynlen2, subt2) ->
| Exp.Sizeof {nbytes=Some nbytes1}, Exp.Sizeof {nbytes=Some nbytes2} ->
Int.equal nbytes1 nbytes2
| Exp.Sizeof {typ=t1; dynamic_length=dynlen1; subtype=subt1},
Exp.Sizeof {typ=t2; dynamic_length=dynlen2; subtype=subt2} ->
Typ.equal t1 t2
&& must_alias_opt dynlen1 dynlen2 m
&& Int.equal (Subtype.compare subt1 subt2) 0
| _, _ -> false
and must_alias_opt : Exp.dynamic_length -> Exp.dynamic_length -> Mem.astate -> bool
= fun dynlen1 dynlen2 m ->
match dynlen1, dynlen2 with
and must_alias_opt : Exp.t option -> Exp.t option -> Mem.astate -> bool
= fun e1_opt e2_opt m ->
match e1_opt, e2_opt with
| Some e1, Some e2 -> must_alias e1 e2 m
| None, None -> true
| _, _ -> false
@ -163,7 +166,8 @@ struct
(* if nested array, add the array blk *)
let arr = Mem.find_heap_set ploc mem in
Val.join (Val.of_pow_loc ploc) arr
| Exp.Sizeof (typ, _, _) -> Val.of_int (sizeof typ)
| Exp.Sizeof {nbytes=Some size} -> Val.of_int size
| Exp.Sizeof {typ; nbytes=None} -> Val.of_int (sizeof typ)
| Exp.Exn _
| Exp.Closure _ -> Val.top_itv
@ -219,10 +223,13 @@ struct
|> Allocsite.make
let eval_array_alloc
: Typ.Procname.t -> CFG.node -> Typ.t -> Itv.t -> Itv.t -> int -> int -> Val.t
= fun pdesc node typ offset size inst_num dimension ->
: Typ.Procname.t -> CFG.node -> Typ.t -> ?stride:int -> Itv.t -> Itv.t -> int -> int -> Val.t
= fun pdesc node typ ?stride:stride0 offset size inst_num dimension ->
let allocsite = get_allocsite pdesc node inst_num dimension in
let stride = sizeof typ |> Itv.of_int in
let int_stride = match stride0 with
| None -> sizeof typ
| Some stride -> stride in
let stride = Itv.of_int int_stride in
ArrayBlk.make allocsite offset size stride
|> Val.of_array_blk

@ -558,7 +558,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
end
| Sil.Call (Some (ret_id, _), Const (Cfun callee_pname),
(target_exp, target_typ) :: (Exp.Sizeof (cast_typ, _, _), _) :: _ , _, _)
(target_exp, target_typ) :: (Exp.Sizeof {typ=cast_typ}, _) :: _ , _, _)
when Typ.Procname.equal callee_pname BuiltinDecl.__cast ->
let lhs_access_path = AccessPath.of_id ret_id (Typ.mk (Tptr (cast_typ, Pk_pointer))) in
let attribute_map =

@ -401,7 +401,7 @@ let translate_block_enumerate block_name stmt_info stmt_list ei =
let parameter = Clang_ast_t.UnaryExprOrTypeTraitExpr
((fresh_stmt_info stmt_info), [],
make_general_expr_info create_unsigned_long_type `RValue `Ordinary,
{ Clang_ast_t.uttei_kind = `SizeOf; Clang_ast_t.uttei_qual_type = type_opt}) in
{ Clang_ast_t.uttei_kind = `SizeOf 1; Clang_ast_t.uttei_qual_type = type_opt}) in
let pointer = di.Clang_ast_t.di_pointer in
let stmt_info = fresh_stmt_info stmt_info in
let malloc_name = CAst_utils.make_name_decl CFrontend_config.malloc in

@ -209,7 +209,8 @@ struct
with Self.SelfClassException class_name ->
let typ = Typ.mk (Tstruct class_name) in
{ empty_res_trans with
exps = [Exp.Sizeof (typ, None, Subtype.exact), Typ.mk (Tint IULong)] }
exps = [Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact},
Typ.mk (Tint IULong)] }
let add_reference_if_glvalue (typ : Typ.t) expr_info =
(* glvalue definition per C++11:*)
@ -414,7 +415,7 @@ struct
let tenv = trans_state.context.CContext.tenv in
let typ = CType_decl.qual_type_to_sil_type tenv expr_info.Clang_ast_t.ei_qual_type in
match unary_expr_or_type_trait_expr_info.Clang_ast_t.uttei_kind with
| `SizeOf ->
| `SizeOf nbytes0 ->
let qt_opt =
CAst_utils.type_from_unary_expr_or_type_trait_expr_info
unary_expr_or_type_trait_expr_info in
@ -422,8 +423,14 @@ struct
match qt_opt with
| Some qt -> CType_decl.qual_type_to_sil_type tenv qt
| None -> typ (* Some default type since the type is missing *) in
{ empty_res_trans with
exps = [(Exp.Sizeof (sizeof_typ, None, Subtype.exact), sizeof_typ)] }
let nbytes = if nbytes0 < 0 then
(* nbytes < 0 when the sizeof cannot be statically determined *)
None
else
Some nbytes0 in
let sizeof_data =
{Exp.typ=sizeof_typ; nbytes; dynamic_length=None; subtype=Subtype.exact} in
{ empty_res_trans with exps = [(Exp.Sizeof sizeof_data, sizeof_typ)] }
| k -> Logging.out
"\nWARNING: Missing translation of Uniry_Expression_Or_Trait of kind: \
%s . Expression ignored, returned -1... \n"
@ -2219,12 +2226,12 @@ struct
let trans_state_pri = PriorityNode.try_claim_priority_node trans_state stmt_info in
let trans_state' = { trans_state_pri with succ_nodes = [] } in
let context = trans_state.context in
let subtypes = Subtype.subtypes_cast in
let subtype = Subtype.subtypes_cast in
let tenv = context.CContext.tenv in
let sil_loc = CLocation.get_sil_location stmt_info context in
let cast_type = CType_decl.qual_type_to_sil_type tenv cast_qual_type in
let sizeof_expr = match cast_type.desc with
| Typ.Tptr (typ, _) -> Exp.Sizeof (typ, None, subtypes)
| Typ.Tptr (typ, _) -> Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype}
| _ -> assert false in
let builtin = Exp.Const (Const.Cfun BuiltinDecl.__cast) in
let stmt = match stmts with [stmt] -> stmt | _ -> assert false in
@ -2288,7 +2295,8 @@ struct
let sil_fun = Exp.Const (Const.Cfun fun_name) in
let ret_id = Ident.create_fresh Ident.knormal in
let void_typ = Typ.mk Tvoid in
let type_info_objc = (Exp.Sizeof (typ, None, Subtype.exact), void_typ) in
let type_info_objc = (Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact},
void_typ) in
let field_name_decl = CAst_utils.make_qual_name_decl ["type_info"; "std"] "__type_name" in
let field_name = CGeneral_utils.mk_class_field_name field_name_decl in
let ret_exp = Exp.Var ret_id in

@ -301,7 +301,8 @@ let create_alloc_instrs sil_loc function_type fname size_exp_opt procname_opt =
| Tptr (styp, Typ.Pk_objc_autoreleasing) ->
function_type, styp
| _ -> CType.add_pointer_to_typ function_type, function_type in
let sizeof_exp_ = Exp.Sizeof (function_type_np, None, Subtype.exact) in
let sizeof_exp_ = Exp.Sizeof {typ=function_type_np; nbytes=None;
dynamic_length=None; subtype=Subtype.exact} in
let sizeof_exp = match size_exp_opt with
| Some exp -> Exp.BinOp (Binop.Mult, sizeof_exp_, exp)
| None -> sizeof_exp_ in
@ -377,7 +378,7 @@ let create_cast_instrs exp cast_from_typ cast_to_typ sil_loc =
let ret_id = Ident.create_fresh Ident.knormal in
let ret_id_typ = Some (ret_id, cast_to_typ) in
let typ = CType.remove_pointer_to_typ cast_to_typ in
let sizeof_exp = Exp.Sizeof (typ, None, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype=Subtype.exact} in
let pname = BuiltinDecl.__objc_cast in
let args = [(exp, cast_from_typ); (sizeof_exp, Typ.mk (Tint Typ.IULong))] in
let stmt_call =

@ -122,8 +122,8 @@ let check_condition tenv case_zero find_canonical_duplicate curr_pdesc
String.equal (Typ.Name.name name) "java.lang.Throwable"
| _ -> false in
let do_instr = function
| Sil.Call (_, Exp.Const (Const.Cfun pn), [_; (Exp.Sizeof(t, _, _), _)], _, _) when
Typ.Procname.equal pn BuiltinDecl.__instanceof && typ_is_throwable t ->
| Sil.Call (_, Exp.Const (Const.Cfun pn), [_; (Exp.Sizeof {typ}, _)], _, _) when
Typ.Procname.equal pn BuiltinDecl.__instanceof && typ_is_throwable typ ->
throwable_found := true
| _ -> () in
let do_node n =

@ -73,7 +73,8 @@ let inhabit_alloc sizeof_typ sizeof_len ret_typ alloc_kind env =
let inhabited_exp = Exp.Var retval in
let call_instr =
let fun_new = fun_exp_from_name alloc_kind in
let sizeof_exp = Exp.Sizeof (sizeof_typ, sizeof_len, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ=sizeof_typ; nbytes=None;
dynamic_length=sizeof_len; subtype=Subtype.exact} in
let args = [(sizeof_exp, Typ.mk (Tptr (ret_typ, Typ.Pk_pointer)))] in
Sil.Call (Some (retval, ret_typ), fun_new, args, env.pc, cf_alloc) in
(inhabited_exp, env_add_instr call_instr env)

@ -624,7 +624,8 @@ let get_array_length context pc expr_list content_type =
(Typ.mk (Tarray (content_type, None)), Some sil_len_expr) in
let array_type, array_len =
List.fold_right ~f:get_array_type_len sil_len_exprs ~init:(content_type, None) in
let array_size = Exp.Sizeof (array_type, array_len, Subtype.exact) in
let array_size = Exp.Sizeof {typ=array_type; nbytes=None;
dynamic_length=array_len; subtype=Subtype.exact} in
(instrs, array_size)
let detect_loop entry_pc impl =
@ -816,7 +817,8 @@ let rec instruction (context : JContext.t) pc instr : translation =
let builtin_new = Exp.Const (Const.Cfun BuiltinDecl.__new) in
let class_type = JTransType.get_class_type program tenv cn in
let class_type_np = JTransType.get_class_type_no_pointer program tenv cn in
let sizeof_exp = Exp.Sizeof (class_type_np, None, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ=class_type_np; nbytes=None;
dynamic_length=None; subtype=Subtype.exact} in
let args = [(sizeof_exp, class_type)] in
let ret_id = Ident.create_fresh Ident.knormal in
let new_instr =
@ -930,7 +932,8 @@ let rec instruction (context : JContext.t) pc instr : translation =
and npe_cn = JBasics.make_cn JConfig.npe_cl in
let class_type = JTransType.get_class_type program tenv npe_cn
and class_type_np = JTransType.get_class_type_no_pointer program tenv npe_cn in
let sizeof_exp = Exp.Sizeof (class_type_np, None, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ=class_type_np; nbytes=None;
dynamic_length=None; subtype=Subtype.exact} in
let args = [(sizeof_exp, class_type)] in
let ret_id = Ident.create_fresh Ident.knormal in
let new_instr =
@ -984,7 +987,8 @@ let rec instruction (context : JContext.t) pc instr : translation =
let out_of_bound_cn = JBasics.make_cn JConfig.out_of_bound_cl in
let class_type = JTransType.get_class_type program tenv out_of_bound_cn
and class_type_np = JTransType.get_class_type_no_pointer program tenv out_of_bound_cn in
let sizeof_exp = Exp.Sizeof (class_type_np, None, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ=class_type_np; nbytes=None;
dynamic_length=None; subtype=Subtype.exact} in
let args = [(sizeof_exp, class_type)] in
let ret_id = Ident.create_fresh Ident.knormal in
let new_instr =
@ -1024,7 +1028,8 @@ let rec instruction (context : JContext.t) pc instr : translation =
and cce_cn = JBasics.make_cn JConfig.cce_cl in
let class_type = JTransType.get_class_type program tenv cce_cn
and class_type_np = JTransType.get_class_type_no_pointer program tenv cce_cn in
let sizeof_exp = Exp.Sizeof (class_type_np, None, Subtype.exact) in
let sizeof_exp = Exp.Sizeof {typ=class_type_np; nbytes=None;
dynamic_length=None; subtype=Subtype.exact} in
let args = [(sizeof_exp, class_type)] in
let ret_id = Ident.create_fresh Ident.knormal in
let new_instr =

@ -72,7 +72,8 @@ let translate_exceptions (context : JContext.t) exit_nodes get_body_nodes handle
let instanceof_builtin = Exp.Const (Const.Cfun BuiltinDecl.__instanceof) in
let args = [
(Exp.Var id_exn_val, Typ.mk (Tptr(exn_type, Typ.Pk_pointer)));
(Exp.Sizeof (exn_type, None, Subtype.exact), Typ.mk Tvoid)] in
(Exp.Sizeof {typ=exn_type; nbytes=None; dynamic_length=None; subtype=Subtype.exact},
Typ.mk Tvoid)] in
Sil.Call
(Some (id_instanceof, Typ.mk (Tint IBool)), instanceof_builtin, args, loc, CallFlags.default) in
let if_kind = Sil.Ik_switch in

@ -374,10 +374,10 @@ and value_type program tenv vt =
(** Translate object types into Exp.Sizeof expressions *)
let sizeof_of_object_type program tenv ot subtypes =
let sizeof_of_object_type program tenv ot subtype =
match (object_type program tenv ot).Typ.desc with
| Typ.Tptr (typ, _) ->
Exp.Sizeof (typ, None, subtypes)
Exp.Sizeof {typ; nbytes=None; dynamic_length=None; subtype}
| _ ->
raise (Type_tranlsation_error "Pointer or array type expected in tenv")

@ -15,18 +15,6 @@ CLANG_OPTIONS = -c
INFER_OPTIONS = -F --project-root $(TESTS_DIR) --no-failures-allowed
INFERPRINT_OPTIONS = --issues-tests
SOURCES = \
trivial.c \
function_call.c \
for_loop.c \
while_loop.c \
do_while.c \
nested_loop.c \
nested_loop_with_label.c \
break_continue_return.c \
goto_loop.c \
inf_loop.c \
prune_constant.c \
prune_alias.c \
SOURCES = $(wildcard *.c)
include $(TESTS_DIR)/clang.make

@ -8,5 +8,6 @@ codetoanalyze/c/bufferoverrun/goto_loop.c, goto_loop, 11, BUFFER_OVERRUN, [Offse
codetoanalyze/c/bufferoverrun/nested_loop.c, nested_loop, 7, BUFFER_OVERRUN, [Offset: [0, 10] Size: [10, 10] @ codetoanalyze/c/bufferoverrun/nested_loop.c:20:7]
codetoanalyze/c/bufferoverrun/nested_loop_with_label.c, nested_loop_with_label, 6, BUFFER_OVERRUN, [Offset: [0, +oo] Size: [10, 10] @ codetoanalyze/c/bufferoverrun/nested_loop_with_label.c:19:5]
codetoanalyze/c/bufferoverrun/prune_alias.c, FP_prune_alias_exp_Ok, 4, BUFFER_OVERRUN, [Offset: [1, 1] Size: [1, 1] @ codetoanalyze/c/bufferoverrun/prune_alias.c:107:5]
codetoanalyze/c/bufferoverrun/sizeof.c, eval_sizeof_bad, 4, BUFFER_OVERRUN, [Offset: [1, 1] Size: [0, 0] @ codetoanalyze/c/bufferoverrun/sizeof.c:13:5]
codetoanalyze/c/bufferoverrun/trivial.c, trivial, 2, BUFFER_OVERRUN, [Offset: [10, 10] Size: [10, 10] @ codetoanalyze/c/bufferoverrun/trivial.c:15:3]
codetoanalyze/c/bufferoverrun/while_loop.c, while_loop, 3, BUFFER_OVERRUN, [Offset: [0, +oo] Size: [10, 10] @ codetoanalyze/c/bufferoverrun/while_loop.c:16:10]

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 - 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.
*/
void eval_sizeof_bad() {
if (sizeof(long long) < 10000) {
// always true
int a[0];
a[1]; // report
}
}

@ -121,7 +121,7 @@ digraph iCFG {
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_16" -> "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_5" ;
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_17" [label="17: DeclStmt \n n$18=_fun_malloc_no_fail(sizeof(signed char):signed char) [line 21]\n *&stop:_Bool*=n$18 [line 21]\n " shape="box"]
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_17" [label="17: DeclStmt \n n$18=_fun_malloc_no_fail(sizeof(signed char1):signed char) [line 21]\n *&stop:_Bool*=n$18 [line 21]\n " shape="box"]
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_17" -> "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_16" ;
@ -202,7 +202,7 @@ digraph iCFG {
"array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_16" -> "array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_5" ;
"array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_17" [label="17: DeclStmt \n n$40=_fun_malloc_no_fail(sizeof(_Bool):_Bool) [line 48]\n *&stop:_Bool*=n$40 [line 48]\n " shape="box"]
"array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_17" [label="17: DeclStmt \n n$40=_fun_malloc_no_fail(sizeof(_Bool8):_Bool) [line 48]\n *&stop:_Bool*=n$40 [line 48]\n " shape="box"]
"array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_17" -> "array_trans#MyBlock#instance.13289a590560d0628a3ae5174e716a32_16" ;

@ -11,7 +11,7 @@ digraph iCFG {
"CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_3" -> "CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_2" ;
"CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_4" [label="4: DeclStmt \n n$0=_fun_malloc_no_fail(sizeof(int):int) [line 16]\n *&x:int*=n$0 [line 16]\n " shape="box"]
"CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_4" [label="4: DeclStmt \n n$0=_fun_malloc_no_fail(sizeof(int32):int) [line 16]\n *&x:int*=n$0 [line 16]\n " shape="box"]
"CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_4" -> "CategoryProcdescMain.ae2ee334c26ccbf8ee413efe5d896611_3" ;

@ -173,7 +173,7 @@ digraph iCFG {
"regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_4" -> "regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_3" ;
"regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_5" [label="5: DeclStmt \n n$39=_fun_malloc_no_fail(sizeof(int):int) [line 88]\n *&x:int*=n$39 [line 88]\n " shape="box"]
"regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_5" [label="5: DeclStmt \n n$39=_fun_malloc_no_fail(sizeof(int32):int) [line 88]\n *&x:int*=n$39 [line 88]\n " shape="box"]
"regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_5" -> "regularLeak#MemoryLeakExample#instance.939a892cee505c3459f2d889292f218b_4" ;
@ -196,7 +196,7 @@ digraph iCFG {
"blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_5" -> "blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_4" ;
"blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_6" [label="6: DeclStmt \n n$48=_fun_malloc_no_fail(sizeof(int):int) [line 94]\n *&x:int*=n$48 [line 94]\n " shape="box"]
"blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_6" [label="6: DeclStmt \n n$48=_fun_malloc_no_fail(sizeof(int32):int) [line 94]\n *&x:int*=n$48 [line 94]\n " shape="box"]
"blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_6" -> "blockCapturedVarLeak#MemoryLeakExample#instance.53bb018bc84d6a696dc756e20b5b3f52_5" ;
@ -219,7 +219,7 @@ digraph iCFG {
"blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_5" -> "blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_4" ;
"blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_6" [label="6: DeclStmt \n n$59=_fun_malloc_no_fail(sizeof(int):int) [line 103]\n *&x:int*=n$59 [line 103]\n " shape="box"]
"blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_6" [label="6: DeclStmt \n n$59=_fun_malloc_no_fail(sizeof(int32):int) [line 103]\n *&x:int*=n$59 [line 103]\n " shape="box"]
"blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_6" -> "blockFreeNoLeakTODO#MemoryLeakExample#instance.745cca07ccdb517734d79c9d7a1eaed8_5" ;

@ -15,7 +15,7 @@ digraph iCFG {
"test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_4" -> "test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_3" ;
"test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_5" [label="5: DeclStmt \n n$2=_fun_malloc_no_fail(sizeof(Person):Person) [line 25]\n *&person:Person*=n$2 [line 25]\n " shape="box"]
"test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_5" [label="5: DeclStmt \n n$2=_fun_malloc_no_fail(sizeof(Person64):Person) [line 25]\n *&person:Person*=n$2 [line 25]\n " shape="box"]
"test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_5" -> "test#NpeMallocC#instance.736ba93f935cc64d6e9c549cc16c07a7_4" ;

Loading…
Cancel
Save