You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
725 lines
27 KiB
725 lines
27 KiB
(*
|
|
* 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.
|
|
*)
|
|
|
|
(** Support for localisation *)
|
|
|
|
module F = Format
|
|
open Utils
|
|
|
|
(** type of string used for localisation *)
|
|
type t = string
|
|
|
|
(** pretty print a localised string *)
|
|
let pp fmt s = Format.fprintf fmt "%s" s
|
|
|
|
(** create a localised string from an ordinary string *)
|
|
let from_string s = s
|
|
|
|
(** convert a localised string to an ordinary string *)
|
|
let to_string s = s
|
|
|
|
(** compare two localised strings *)
|
|
let compare (s1: string) (s2: string) = Pervasives.compare s1 s2
|
|
|
|
let activity_leak = "ACTIVITY_LEAK"
|
|
let analysis_stops = "ANALYSIS_STOPS"
|
|
let array_out_of_bounds_l1 = "ARRAY_OUT_OF_BOUNDS_L1"
|
|
let array_out_of_bounds_l2 = "ARRAY_OUT_OF_BOUNDS_L2"
|
|
let array_out_of_bounds_l3 = "ARRAY_OUT_OF_BOUNDS_L3"
|
|
let class_cast_exception = "CLASS_CAST_EXCEPTION"
|
|
let comparing_floats_for_equality = "COMPARING_FLOAT_FOR_EQUALITY"
|
|
let condition_is_assignment = "CONDITION_IS_ASSIGNMENT"
|
|
let condition_always_false = "CONDITION_ALWAYS_FALSE"
|
|
let condition_always_true = "CONDITION_ALWAYS_TRUE"
|
|
let dangling_pointer_dereference = "DANGLING_POINTER_DEREFERENCE"
|
|
let deallocate_stack_variable = "DEALLOCATE_STACK_VARIABLE"
|
|
let deallocate_static_memory = "DEALLOCATE_STATIC_MEMORY"
|
|
let deallocation_mismatch = "DEALLOCATION_MISMATCH"
|
|
let divide_by_zero = "DIVIDE_BY_ZERO"
|
|
let field_not_null_checked = "IVAR_NOT_NULL_CHECKED"
|
|
let inherently_dangerous_function = "INHERENTLY_DANGEROUS_FUNCTION"
|
|
let memory_leak = "MEMORY_LEAK"
|
|
let null_dereference = "NULL_DEREFERENCE"
|
|
let parameter_not_null_checked = "PARAMETER_NOT_NULL_CHECKED"
|
|
let null_test_after_dereference = "NULL_TEST_AFTER_DEREFERENCE"
|
|
let pointer_size_mismatch = "POINTER_SIZE_MISMATCH"
|
|
let precondition_not_found = "PRECONDITION_NOT_FOUND"
|
|
let precondition_not_met = "PRECONDITION_NOT_MET"
|
|
let premature_nil_termination = "PREMATURE_NIL_TERMINATION_ARGUMENT"
|
|
let resource_leak = "RESOURCE_LEAK"
|
|
let retain_cycle = "RETAIN_CYCLE"
|
|
let return_value_ignored = "RETURN_VALUE_IGNORED"
|
|
let return_expression_required = "RETURN_EXPRESSION_REQUIRED"
|
|
let return_statement_missing = "RETURN_STATEMENT_MISSING"
|
|
let skip_function = "SKIP_FUNCTION"
|
|
let skip_pointer_dereference = "SKIP_POINTER_DEREFERENCE"
|
|
let stack_variable_address_escape = "STACK_VARIABLE_ADDRESS_ESCAPE"
|
|
let tainted_value_reaching_sensitive_function = "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION"
|
|
let unary_minus_applied_to_unsigned_expression = "UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION"
|
|
let uninitialized_value = "UNINITIALIZED_VALUE"
|
|
let use_after_free = "USE_AFTER_FREE"
|
|
|
|
(** description field of error messages: descriptions, advice and tags *)
|
|
type error_desc = string list * string option * (string * string) list
|
|
|
|
(** empty error description *)
|
|
let no_desc: error_desc = [], None, []
|
|
|
|
(** verbatim desc from a string, not to be used for user-visible descs *)
|
|
let verbatim_desc s = [s], None, []
|
|
|
|
let custom_desc s tags = [s], None, tags
|
|
|
|
let custom_desc_with_advice description advice tags =
|
|
[description], Some advice, tags
|
|
|
|
(** pretty print an error description *)
|
|
let pp_error_desc fmt (l, _, s) =
|
|
let pp_item fmt s = F.fprintf fmt "%s" s in
|
|
pp_seq pp_item fmt l
|
|
|
|
(** pretty print an error advice *)
|
|
let pp_error_advice fmt (_, advice, _) =
|
|
match advice with
|
|
| Some advice -> F.fprintf fmt "%s" advice
|
|
| None -> ()
|
|
|
|
(** pretty print an error description *)
|
|
let pp_error_desc fmt (l, _, _) =
|
|
let pp_item fmt s = F.fprintf fmt "%s" s in
|
|
pp_seq pp_item fmt l
|
|
|
|
(** get tags of error description *)
|
|
let error_desc_get_tags (_, _, tags) = tags
|
|
|
|
module Tags = struct
|
|
let accessed_line = "accessed_line" (* line where value was last accessed *)
|
|
let alloc_function = "alloc_function" (* allocation function used *)
|
|
let alloc_call = "alloc_call" (* call in the current procedure which triggers the allocation *)
|
|
let alloc_line = "alloc_line" (* line of alloc_call *)
|
|
let array_index = "array_index" (* index of the array *)
|
|
let array_size = "array_size" (* size of the array *)
|
|
let assigned_line = "assigned_line" (* line where value was last assigned *)
|
|
let bucket = "bucket" (* bucket to classify likelyhood of real bug *)
|
|
let call_procedure = "call_procedure" (* name of the procedure called *)
|
|
let call_line = "call_line" (* line of call_procedure *)
|
|
let dealloc_function = "dealloc_function" (* deallocation function used *)
|
|
let dealloc_call = "dealloc_call" (* call in the current procedure which triggers the deallocation *)
|
|
let dealloc_line = "dealloc_line" (* line of dealloc_call *)
|
|
let dereferenced_line = "dereferenced_line" (* line where value was dereferenced *)
|
|
let escape_to = "escape_to" (* expression wher a value escapes to *)
|
|
let line = "line" (* line of the error *)
|
|
let type1 = "type1" (* 1st Java type *)
|
|
let type2 = "type2" (* 2nd Java type *)
|
|
let value = "value" (* string describing a C value, e.g. "x.date" *)
|
|
let parameter_not_null_checked = "parameter_not_null_checked" (* describes a NPE that comes from parameter not nullable *)
|
|
let field_not_null_checked = "field_not_null_checked" (* describes a NPE that comes from field not nullable *)
|
|
let nullable_src = "nullable_src" (* @Nullable-annoted field/param/retval that causes a warning *)
|
|
let create () = ref []
|
|
let add tags tag value = tags := (tag, value) :: !tags
|
|
let update tags tag value =
|
|
let tags' = list_filter (fun (t, v) -> t <> tag) tags in
|
|
(tag, value) :: tags'
|
|
let get tags tag =
|
|
try
|
|
let (_, v) = list_find (fun (t, _) -> t = tag) tags in
|
|
Some v
|
|
with Not_found -> None
|
|
end
|
|
|
|
module BucketLevel = struct
|
|
let b1 = "B1" (* highest likelyhood *)
|
|
let b2 = "B2"
|
|
let b3 = "B3"
|
|
let b4 = "B4"
|
|
let b5 = "B5" (* lowest likelyhood *)
|
|
end
|
|
|
|
(** takes in input a tag to extract from the given error_desc
|
|
and returns its value *)
|
|
let error_desc_extract_tag_value (_, _, tags) tag_to_extract =
|
|
let find_value tag v =
|
|
match v with
|
|
| (t, _) when t = tag -> true
|
|
| _ -> false in
|
|
try
|
|
let _, s = list_find (find_value tag_to_extract) tags in
|
|
s
|
|
with Not_found -> ""
|
|
|
|
let error_desc_to_tag_value_pairs (_, _, tags) = tags
|
|
|
|
(** returns the content of the value tag of the error_desc *)
|
|
let error_desc_get_tag_value error_desc = error_desc_extract_tag_value error_desc Tags.value
|
|
|
|
(** returns the content of the call_procedure tag of the error_desc *)
|
|
let error_desc_get_tag_call_procedure error_desc = error_desc_extract_tag_value error_desc Tags.call_procedure
|
|
|
|
(** get the bucket value of an error_desc, if any *)
|
|
let error_desc_get_bucket (_, _, tags) =
|
|
Tags.get tags Tags.bucket
|
|
|
|
(** set the bucket value of an error_desc; the boolean indicates where the bucket should be shown in the message *)
|
|
let error_desc_set_bucket (l, advice, tags) bucket show_in_message =
|
|
let tags' = Tags.update tags Tags.bucket bucket in
|
|
let l' =
|
|
if show_in_message = false then l
|
|
else ("[" ^ bucket ^ "]") :: l in
|
|
(l', advice, tags')
|
|
|
|
(** get the value tag, if any *)
|
|
let get_value_line_tag tags =
|
|
try
|
|
let value = snd (list_find (fun (_tag, value) -> _tag = Tags.value) tags) in
|
|
let line = snd (list_find (fun (_tag, value) -> _tag = Tags.line) tags) in
|
|
Some [value; line]
|
|
with Not_found -> None
|
|
|
|
(** extract from desc a value on which to apply polymorphic hash and equality *)
|
|
let desc_get_comparable (sl, advice, tags) =
|
|
match get_value_line_tag tags with
|
|
| Some sl' -> sl'
|
|
| None -> sl
|
|
|
|
(** hash function for error_desc *)
|
|
let error_desc_hash desc =
|
|
Hashtbl.hash (desc_get_comparable desc)
|
|
|
|
(** equality for error_desc *)
|
|
let error_desc_equal desc1 desc2 = (desc_get_comparable desc1) = (desc_get_comparable desc2)
|
|
|
|
let _line_tag tags tag loc =
|
|
let line_str = string_of_int loc.Location.line in
|
|
Tags.add tags tag line_str;
|
|
let s = "line " ^ line_str in
|
|
if (loc.Location.col != -1) then
|
|
let col_str = string_of_int loc.Location.col in
|
|
s ^ ", column " ^ col_str
|
|
else s
|
|
|
|
let at_line_tag tags tag loc =
|
|
"at " ^ _line_tag tags tag loc
|
|
|
|
let _line tags loc =
|
|
_line_tag tags Tags.line loc
|
|
|
|
let at_line tags loc =
|
|
at_line_tag tags Tags.line loc
|
|
|
|
let call_to tags proc_name =
|
|
let proc_name_str = Procname.to_simplified_string proc_name in
|
|
Tags.add tags Tags.call_procedure proc_name_str;
|
|
"call to " ^ proc_name_str
|
|
|
|
let call_to_at_line tags proc_name loc =
|
|
(call_to tags proc_name) ^ " " ^ at_line_tag tags Tags.call_line loc
|
|
|
|
let by_call_to tags proc_name =
|
|
"by " ^ call_to tags proc_name
|
|
|
|
let by_call_to_ra tags ra =
|
|
"by " ^ call_to_at_line tags ra.Sil.ra_pname ra.Sil.ra_loc
|
|
|
|
let mem_dyn_allocated = "memory dynamically allocated"
|
|
let res_acquired = "resource acquired"
|
|
let lock_acquired = "lock acquired"
|
|
let released = "released"
|
|
let reachable = "reachable"
|
|
|
|
(** dereference strings used to explain a dereference action in an error message *)
|
|
type deref_str =
|
|
{ tags : (string * string) list ref; (** tags for the error description *)
|
|
value_pre: string option; (** string printed before the value being dereferenced *)
|
|
value_post: string option; (** string printed after the value being dereferenced *)
|
|
problem_str: string; (** description of the problem *) }
|
|
|
|
let pointer_or_object () =
|
|
if !Config.curr_language = Config.Java then "object" else "pointer"
|
|
|
|
let _deref_str_null proc_name_opt _problem_str tags =
|
|
let problem_str = match proc_name_opt with
|
|
| Some proc_name ->
|
|
_problem_str ^ " " ^ by_call_to tags proc_name
|
|
| None -> _problem_str in
|
|
{ tags = tags;
|
|
value_pre = Some (pointer_or_object ());
|
|
value_post = None;
|
|
problem_str = problem_str; }
|
|
|
|
(** dereference strings for null dereference *)
|
|
let deref_str_null proc_name_opt =
|
|
let problem_str = "could be null and is dereferenced" in
|
|
_deref_str_null proc_name_opt problem_str (Tags.create ())
|
|
|
|
(** dereference strings for null dereference due to Nullable annotation *)
|
|
let deref_str_nullable proc_name_opt nullable_obj_str =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.nullable_src nullable_obj_str;
|
|
(* to be completed once we know if the deref'd expression is directly or transitively @Nullable*)
|
|
let problem_str = "" in
|
|
_deref_str_null proc_name_opt problem_str tags
|
|
|
|
(** dereference strings for nonterminal nil arguments in c/objc variadic methods *)
|
|
let deref_str_nil_argument_in_variadic_method pn total_args arg_number =
|
|
let tags = Tags.create () in
|
|
let function_method, nil_null =
|
|
if Procname.is_c_method pn then ("method", "nil") else ("function", "null") in
|
|
let problem_str =
|
|
Printf.sprintf
|
|
"could be %s which results in a call to %s with %d arguments instead of %d \
|
|
(%s indicates that the last argument of this variadic %s has been reached)"
|
|
nil_null (Procname.to_simplified_string pn) arg_number (total_args - 1) nil_null function_method in
|
|
_deref_str_null None problem_str tags
|
|
|
|
(** dereference strings for an undefined value coming from the given procedure *)
|
|
let deref_str_undef (proc_name, loc) =
|
|
let tags = Tags.create () in
|
|
let proc_name_str = Procname.to_simplified_string proc_name in
|
|
Tags.add tags Tags.call_procedure proc_name_str;
|
|
{ tags = tags;
|
|
value_pre = Some (pointer_or_object ());
|
|
value_post = None;
|
|
problem_str = "could be assigned by a call to skip function " ^ proc_name_str ^
|
|
at_line_tag tags Tags.call_line loc ^ " and is dereferenced or freed"; }
|
|
|
|
(** dereference strings for a freed pointer dereference *)
|
|
let deref_str_freed ra =
|
|
let tags = Tags.create () in
|
|
let freed_or_closed_by_call =
|
|
let freed_or_closed = match ra.Sil.ra_res with
|
|
| Sil.Rmemory _ -> "freed"
|
|
| Sil.Rfile -> "closed"
|
|
| Sil.Rignore -> "freed"
|
|
| Sil.Rlock -> "locked" in
|
|
freed_or_closed ^ " " ^ by_call_to_ra tags ra in
|
|
{ tags = tags;
|
|
value_pre = Some (pointer_or_object ());
|
|
value_post = None;
|
|
problem_str = "was " ^ freed_or_closed_by_call ^ " and is dereferenced or freed" }
|
|
|
|
(** dereference strings for a dangling pointer dereference *)
|
|
let deref_str_dangling dangling_kind_opt =
|
|
let dangling_kind_prefix = match dangling_kind_opt with
|
|
| Some Sil.DAuninit -> "uninitialized "
|
|
| Some Sil.DAaddr_stack_var -> "deallocated stack "
|
|
| Some Sil.DAminusone -> "-1 "
|
|
| None -> "" in
|
|
{ tags = Tags.create ();
|
|
value_pre = Some (dangling_kind_prefix ^ (pointer_or_object ()));
|
|
value_post = None;
|
|
problem_str = "could be dangling and is dereferenced or freed"; }
|
|
|
|
(** dereference strings for a pointer size mismatch *)
|
|
let deref_str_pointer_size_mismatch typ_from_instr typ_of_object =
|
|
let str_from_typ typ =
|
|
let pp f () = Sil.pp_typ_full pe_text f typ in
|
|
pp_to_string pp () in
|
|
{ tags = Tags.create ();
|
|
value_pre = Some (pointer_or_object ());
|
|
value_post = Some ("of type " ^ str_from_typ typ_from_instr);
|
|
problem_str = "could be used to access an object of smaller type " ^ str_from_typ typ_of_object; }
|
|
|
|
(** dereference strings for an array out of bound access *)
|
|
let deref_str_array_bound size_opt index_opt =
|
|
let tags = Tags.create () in
|
|
let size_str_opt = match size_opt with
|
|
| Some n ->
|
|
let n_str = Sil.Int.to_string n in
|
|
Tags.add tags Tags.array_size n_str;
|
|
Some ("of size " ^ n_str)
|
|
| None -> None in
|
|
let index_str = match index_opt with
|
|
| Some n ->
|
|
let n_str = Sil.Int.to_string n in
|
|
Tags.add tags Tags.array_index n_str;
|
|
"index " ^ n_str
|
|
| None -> "an index" in
|
|
{ tags = tags;
|
|
value_pre = Some "array";
|
|
value_post = size_str_opt;
|
|
problem_str = "could be accessed with " ^ index_str ^ " out of bounds"; }
|
|
|
|
(** dereference strings for an uninitialized access whose lhs has the given attribute *)
|
|
let deref_str_uninitialized alloc_att_opt =
|
|
let tags = Tags.create () in
|
|
let creation_str = match alloc_att_opt with
|
|
| Some (Sil.Aresource ({ Sil.ra_kind = Sil.Racquire } as ra)) ->
|
|
"after allocation " ^ by_call_to_ra tags ra
|
|
| _ -> "after declaration" in
|
|
{ tags = tags;
|
|
value_pre = Some "value";
|
|
value_post = None;
|
|
problem_str = "was not initialized " ^ creation_str ^ " and is used"; }
|
|
|
|
(** Java unchecked exceptions errors *)
|
|
let java_unchecked_exn_desc proc_name exn_name pre_str : error_desc =
|
|
([Procname.to_string proc_name;
|
|
"can throw "^(Mangled.to_string exn_name);
|
|
"whenever "^pre_str], None, [])
|
|
|
|
let desc_activity_leak pname activity_typ fieldname : error_desc =
|
|
let pname_str = Procname.java_get_class pname ^ "." ^ Procname.java_get_method pname in
|
|
(* intentionally omit space; [typ_to_string] adds an extra space *)
|
|
let activity_str = Sil.typ_to_string activity_typ ^ "may leak via" in
|
|
let fld_str = Ident.fieldname_to_string fieldname in
|
|
let leak_msg =
|
|
if fld_str = "android.os.Handler.sFakeHandlerQueue" then "call to Handler.postDelayed"
|
|
else "assignment to static field " ^ fld_str in
|
|
(["Activity"; activity_str; leak_msg; "during call to"; pname_str] , None, [])
|
|
|
|
let desc_assertion_failure loc : error_desc =
|
|
(["could be raised"; at_line (Tags.create ()) loc], None, [])
|
|
|
|
(** type of access *)
|
|
type access =
|
|
| Last_assigned of int * bool (* line, null_case_flag *)
|
|
| Last_accessed of int * bool (* line, is_nullable flag *)
|
|
| Initialized_automatically
|
|
| Returned_from_call of int
|
|
|
|
let dereference_string deref_str value_str access_opt loc =
|
|
let tags = deref_str.tags in
|
|
Tags.add tags Tags.value value_str;
|
|
let is_call_access = match access_opt with
|
|
| Some (Returned_from_call _) -> true
|
|
| _ -> false in
|
|
let value_desc =
|
|
String.concat "" [
|
|
(match deref_str.value_pre with Some s -> s ^ " " | _ -> "");
|
|
(if is_call_access then "returned by " else "");
|
|
value_str;
|
|
(match deref_str.value_post with Some s -> " " ^ s | _ -> "")] in
|
|
let access_desc = match access_opt with
|
|
| None ->
|
|
[]
|
|
| Some (Last_accessed (n, _)) ->
|
|
let line_str = string_of_int n in
|
|
Tags.add tags Tags.accessed_line line_str;
|
|
["last accessed on line " ^ line_str]
|
|
| Some (Last_assigned (n, ncf)) ->
|
|
let line_str = string_of_int n in
|
|
Tags.add tags Tags.assigned_line line_str;
|
|
["last assigned on line " ^ line_str]
|
|
| Some (Returned_from_call _) -> []
|
|
| Some Initialized_automatically ->
|
|
["initialized automatically"] in
|
|
let problem_desc =
|
|
let problem_str =
|
|
match Tags.get !tags Tags.nullable_src with
|
|
| Some nullable_src ->
|
|
if nullable_src = value_str then "is annotated with @Nullable and is dereferenced without a null check"
|
|
else "is indirectly marked @Nullable (source: " ^ nullable_src ^ ") and is dereferenced without a null check"
|
|
| None -> deref_str.problem_str in
|
|
[(problem_str ^ " " ^ at_line tags loc)] in
|
|
value_desc:: access_desc @ problem_desc, None, !tags
|
|
|
|
let parameter_field_not_null_checked_desc desc exp =
|
|
let parameter_not_nullable_desc var =
|
|
let var_s = Sil.pvar_to_string var in
|
|
let param_not_null_desc =
|
|
"Parameter "^var_s^" is not checked for null, there could be a null pointer dereference:" in
|
|
match desc with
|
|
| descriptions, advice, tags ->
|
|
param_not_null_desc:: descriptions, advice, (Tags.parameter_not_null_checked, var_s):: tags in
|
|
let field_not_nullable_desc exp =
|
|
let rec exp_to_string exp =
|
|
match exp with
|
|
| Sil.Lfield (exp', field, typ) -> (exp_to_string exp')^" -> "^(Ident.fieldname_to_string field)
|
|
| Sil.Lvar pvar -> Mangled.to_string (Sil.pvar_get_name pvar)
|
|
| _ -> "" in
|
|
let var_s = exp_to_string exp in
|
|
let field_not_null_desc =
|
|
"Instance variable "^var_s^" is not checked for null, there could be a null pointer dereference:" in
|
|
match desc with
|
|
| descriptions, advice, tags ->
|
|
field_not_null_desc:: descriptions, advice, (Tags.field_not_null_checked, var_s):: tags in
|
|
match exp with
|
|
| Sil.Lvar var -> parameter_not_nullable_desc var
|
|
| Sil.Lfield _ -> field_not_nullable_desc exp
|
|
| _ -> desc
|
|
|
|
let has_tag desc tag =
|
|
match desc with
|
|
| descriptions, advice, tags ->
|
|
list_exists (fun (tag', value) -> tag = tag') tags
|
|
|
|
let is_parameter_not_null_checked_desc desc = has_tag desc Tags.parameter_not_null_checked
|
|
|
|
let is_field_not_null_checked_desc desc = has_tag desc Tags.field_not_null_checked
|
|
|
|
let is_parameter_field_not_null_checked_desc desc =
|
|
is_parameter_not_null_checked_desc desc ||
|
|
is_field_not_null_checked_desc desc
|
|
|
|
let desc_allocation_mismatch alloc dealloc =
|
|
let tags = Tags.create () in
|
|
let using is_alloc (primitive_pname, called_pname, loc) =
|
|
let tag_fun, tag_call, tag_line =
|
|
if is_alloc then Tags.alloc_function, Tags.alloc_call, Tags.alloc_line
|
|
else Tags.dealloc_function, Tags.dealloc_call, Tags.dealloc_line in
|
|
Tags.add tags tag_fun (Procname.to_simplified_string primitive_pname);
|
|
Tags.add tags tag_call (Procname.to_simplified_string called_pname);
|
|
Tags.add tags tag_line (string_of_int loc.Location.line);
|
|
let by_call =
|
|
if Procname.equal primitive_pname called_pname then ""
|
|
else " by call to " ^ Procname.to_simplified_string called_pname in
|
|
"using " ^ Procname.to_simplified_string primitive_pname ^ by_call ^ " " ^ at_line (Tags.create ()) (* ignore the tag *) loc in
|
|
let description = Format.sprintf
|
|
"%s %s is deallocated %s"
|
|
mem_dyn_allocated
|
|
(using true alloc)
|
|
(using false dealloc) in
|
|
[description], None, !tags
|
|
|
|
let desc_comparing_floats_for_equality loc =
|
|
let tags = Tags.create () in
|
|
["Comparing floats for equality " ^ at_line tags loc], None, !tags
|
|
|
|
let desc_condition_is_assignment loc =
|
|
let tags = Tags.create () in
|
|
["Boolean condition is an assignment " ^ at_line tags loc], None, !tags
|
|
|
|
let desc_condition_always_true_false i cond_str_opt loc =
|
|
let tags = Tags.create () in
|
|
let value = match cond_str_opt with
|
|
| None -> ""
|
|
| Some s -> s in
|
|
let tt_ff = if Sil.Int.iszero i then "false" else "true" in
|
|
Tags.add tags Tags.value value;
|
|
let description = Format.sprintf
|
|
"Boolean condition %s is always %s %s"
|
|
(if value = "" then "" else " " ^ value)
|
|
tt_ff
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_deallocate_stack_variable var_str proc_name loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value var_str;
|
|
let description = Format.sprintf
|
|
"Stack variable %s is freed by a %s"
|
|
var_str
|
|
(call_to_at_line tags proc_name loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_deallocate_static_memory const_str proc_name loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value const_str;
|
|
let description = Format.sprintf
|
|
"Constant string %s is freed by a %s"
|
|
const_str
|
|
(call_to_at_line tags proc_name loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_class_cast_exception pname_opt typ_str1 typ_str2 exp_str_opt loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.type1 typ_str1;
|
|
Tags.add tags Tags.type2 typ_str2;
|
|
let in_expression = match exp_str_opt with
|
|
| Some exp_str ->
|
|
Tags.add tags Tags.value exp_str;
|
|
" in expression " ^ exp_str ^ " "
|
|
| None -> " " in
|
|
let at_line' () = match pname_opt with
|
|
| Some proc_name -> "in " ^ call_to_at_line tags proc_name loc
|
|
| None -> at_line tags loc in
|
|
let description = Format.sprintf
|
|
"%s cannot be cast to %s %s %s"
|
|
typ_str1
|
|
typ_str2
|
|
in_expression
|
|
(at_line' ()) in
|
|
[description], None, !tags
|
|
|
|
let desc_divide_by_zero expr_str loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value expr_str;
|
|
let description = Format.sprintf
|
|
"Expression %s could be zero %s"
|
|
expr_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_leak value_str_opt resource_opt resource_action_opt loc bucket_opt =
|
|
let tags = Tags.create () in
|
|
let () = match bucket_opt with
|
|
| Some bucket ->
|
|
Tags.add tags Tags.bucket bucket;
|
|
| None -> () in
|
|
let value_str = match value_str_opt with
|
|
| None -> ""
|
|
| Some s ->
|
|
Tags.add tags Tags.value s;
|
|
s in
|
|
let xxx_allocated_to =
|
|
let desc_str =
|
|
let _to = if value_str_opt = None then "" else " to " in
|
|
let _on = if value_str_opt = None then "" else " on " in
|
|
match resource_opt with
|
|
| Some Sil.Rmemory _ -> mem_dyn_allocated ^ _to ^ value_str
|
|
| Some Sil.Rfile -> res_acquired ^ _to ^ value_str
|
|
| Some Sil.Rlock -> lock_acquired ^ _on ^ value_str
|
|
| Some Sil.Rignore
|
|
| None -> if value_str_opt = None then "memory" else value_str in
|
|
if desc_str = "" then [] else [desc_str] in
|
|
let by_call_to = match resource_action_opt with
|
|
| Some ra -> [(by_call_to_ra tags ra)]
|
|
| None -> [] in
|
|
let is_not_rxxx_after =
|
|
let rxxx = match resource_opt with
|
|
| Some Sil.Rmemory _ -> reachable
|
|
| Some Sil.Rfile
|
|
| Some Sil.Rlock -> released
|
|
| Some Sil.Rignore
|
|
| None -> reachable in
|
|
[("is not " ^ rxxx ^ " after " ^ _line tags loc)] in
|
|
let bucket_str =
|
|
match bucket_opt with
|
|
| Some bucket when !Config.show_ml_buckets -> bucket
|
|
| _ -> "" in
|
|
bucket_str :: xxx_allocated_to @ by_call_to @ is_not_rxxx_after, None, !tags
|
|
|
|
(** kind of precondition not met *)
|
|
type pnm_kind =
|
|
| Pnm_bounds
|
|
| Pnm_dangling
|
|
|
|
let desc_precondition_not_met kind proc_name loc =
|
|
let tags = Tags.create () in
|
|
let kind_str = match kind with
|
|
| None -> []
|
|
| Some Pnm_bounds -> ["possible array out of bounds"]
|
|
| Some Pnm_dangling -> ["possible dangling pointer dereference"] in
|
|
kind_str @ ["in " ^ call_to_at_line tags proc_name loc], None, !tags
|
|
|
|
let desc_null_test_after_dereference expr_str line loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.dereferenced_line (string_of_int line);
|
|
Tags.add tags Tags.value expr_str;
|
|
let description = Format.sprintf
|
|
"Pointer %s was dereferenced at line %d and is tested for null %s"
|
|
expr_str
|
|
line
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_return_expression_required typ_str loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value typ_str;
|
|
let description = Format.sprintf
|
|
"Return statement requires an expression of type %s %s"
|
|
typ_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_retain_cycle prop cycle loc =
|
|
Logging.d_strln "Proposition with retain cycle:";
|
|
Prop.d_prop prop; Logging.d_strln "";
|
|
let ct = ref 1 in
|
|
let tags = Tags.create () in
|
|
let str_cycle = ref "" in
|
|
let remove_old s =
|
|
match Str.split_delim (Str.regexp_string "&old_") s with
|
|
| [_; s'] -> s'
|
|
| _ -> s in
|
|
let do_edge ((se,_), f, se') =
|
|
match se with
|
|
| Sil.Eexp(Sil.Lvar pvar, _) when Sil.pvar_equal pvar Sil.block_pvar ->
|
|
str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") a block capturing "^(Ident.fieldname_to_string f)^"; ";
|
|
ct:=!ct +1;
|
|
| Sil.Eexp(Sil.Lvar pvar as e, _) ->
|
|
let e_str = Sil.exp_to_string e in
|
|
let e_str = if Sil.pvar_is_seed pvar then
|
|
remove_old e_str
|
|
else e_str in
|
|
str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") object "^e_str^" retaining "^e_str^"."^(Ident.fieldname_to_string f)^", ";
|
|
ct:=!ct +1
|
|
| Sil.Eexp(Sil.Sizeof(typ, _), _) ->
|
|
str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") an object of "^(Sil.typ_to_string typ)^" retaining another object via instance variable "^(Ident.fieldname_to_string f)^", ";
|
|
ct:=!ct +1
|
|
| _ -> () in
|
|
list_iter do_edge cycle;
|
|
let desc = Format.sprintf "Retain cycle involving the following objects: %s %s"
|
|
!str_cycle (at_line tags loc) in
|
|
[desc], None, !tags
|
|
|
|
let desc_return_statement_missing loc =
|
|
let tags = Tags.create () in
|
|
["Return statement missing " ^ at_line tags loc], None, !tags
|
|
|
|
let desc_return_value_ignored proc_name loc =
|
|
let tags = Tags.create () in
|
|
["after " ^ call_to_at_line tags proc_name loc], None, !tags
|
|
|
|
let desc_unary_minus_applied_to_unsigned_expression expr_str_opt typ_str loc =
|
|
let tags = Tags.create () in
|
|
let expression = match expr_str_opt with
|
|
| Some s ->
|
|
Tags.add tags Tags.value s;
|
|
"expression " ^ s
|
|
| None -> "an expression" in
|
|
let description = Format.sprintf
|
|
"A unary minus is applied to %s of type %s %s"
|
|
expression
|
|
typ_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_skip_function proc_name =
|
|
let tags = Tags.create () in
|
|
let proc_name_str = Procname.to_string proc_name in
|
|
Tags.add tags Tags.value proc_name_str;
|
|
[proc_name_str], None, !tags
|
|
|
|
let desc_inherently_dangerous_function proc_name =
|
|
let proc_name_str = Procname.to_string proc_name in
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value proc_name_str;
|
|
[proc_name_str], None, !tags
|
|
|
|
let desc_stack_variable_address_escape expr_str addr_dexp_str loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value expr_str;
|
|
let escape_to_str = match addr_dexp_str with
|
|
| Some s ->
|
|
Tags.add tags Tags.escape_to s;
|
|
"to " ^ s ^ " "
|
|
| None -> "" in
|
|
let description = Format.sprintf
|
|
"Address of stack variable %s escapes %s%s"
|
|
expr_str
|
|
escape_to_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_tainted_value_reaching_sensitive_function expr_str loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value expr_str;
|
|
let description = Format.sprintf
|
|
"Value %s can be tainted and is reaching sensitive function %s"
|
|
expr_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|
|
|
|
let desc_uninitialized_dangling_pointer_deref deref expr_str loc =
|
|
let tags = Tags.create () in
|
|
Tags.add tags Tags.value expr_str;
|
|
let prefix = match deref.value_pre with
|
|
| Some s -> s
|
|
| _ -> "" in
|
|
let description =
|
|
Format.sprintf
|
|
"%s %s %s %s"
|
|
prefix
|
|
expr_str
|
|
deref.problem_str
|
|
(at_line tags loc) in
|
|
[description], None, !tags
|