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.

398 lines
12 KiB

(*
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
module F = Format
module L = Logging
(** Annotations. *)
let any_thread = "AnyThread"
let assume_thread_safe = "AssumeThreadSafe"
let bind = "Bind"
let bind_view = "BindView"
let bind_array = "BindArray"
let bind_bitmap = "BindBitmap"
let bind_drawable = "BindDrawable"
let bind_string = "BindString"
let camel_nonnull = "NonNull"
let expensive = "Expensive"
let false_on_null = "FalseOnNull"
let for_ui_thread = "ForUiThread"
let for_non_ui_thread = "ForNonUiThread"
let guarded_by = "GuardedBy"
let ignore_allocations = "IgnoreAllocations"
let initializer_ = "Initializer"
let inject = "Inject"
let inject_view = "InjectView"
let integrity_source = "IntegritySource"
let integrity_sink = "IntegritySink"
let mutable_ = "Mutable"
let nonnull = "Nonnull"
let no_allocation = "NoAllocation"
let nullable = "Nullable"
let on_bind = "OnBind"
let on_event = "OnEvent"
let on_mount = "OnMount"
let on_unbind = "OnUnbind"
let on_unmount = "OnUnmount"
let notnull = "NotNull"
let not_thread_safe = "NotThreadSafe"
let performance_critical = "PerformanceCritical"
let present = "Present"
let privacy_source = "PrivacySource"
let privacy_sink = "PrivacySink"
let strict = "com.facebook.infer.annotation.Strict"
let suppress_lint = "SuppressLint"
let suppress_view_nullability = "SuppressViewNullability"
let thread_confined = "ThreadConfined"
let thread_safe = "ThreadSafe"
let thread_safe_method = "ThreadSafeMethod"
let true_on_null = "TrueOnNull"
let ui_thread = "UiThread"
let verify_annotation = "com.facebook.infer.annotation.Verify"
let visibleForTesting = "com.google.common.annotations.VisibleForTesting"
let volatile = "volatile"
(** Method signature with annotations. *)
type annotated_signature = {
ret : Annot.Item.t * Typ.t; (** Annotated return type. *)
params: (Mangled.t * Annot.Item.t * Typ.t) list (** Annotated parameters. *)
} [@@deriving compare]
let ia_iter f =
let ann_iter (a, _) = f a in
IList.iter ann_iter
let ma_iter f ((ia, ial) : Annot.Method.t) =
IList.iter (ia_iter f) (ia:: ial)
let ma_has_annotation_with
(ma: Annot.Method.t)
(predicate: Annot.t -> bool): bool =
let found = ref false in
ma_iter
(fun a -> if predicate a then found := true)
ma;
!found
let ia_has_annotation_with
(ia: Annot.Item.t)
(predicate: Annot.t -> bool): bool =
let found = ref false in
ia_iter
(fun a -> if predicate a then found := true)
ia;
!found
(** [annot_ends_with annot ann_name] returns true if the class name of [annot], without the package,
is equal to [ann_name] *)
let annot_ends_with annot ann_name =
match String.rsplit2 annot.Annot.class_name ~on:'.' with
| None -> String.equal annot.Annot.class_name ann_name
| Some (_, annot_class_name) -> String.equal annot_class_name ann_name
(** Check if there is an annotation in [ia] which ends with the given name *)
let ia_ends_with ia ann_name =
let found = ref false in
ia_iter (fun a -> if annot_ends_with a ann_name then found := true) ia;
!found
let ia_contains ia ann_name =
let found = ref false in
ia_iter (fun a -> if String.equal ann_name a.Annot.class_name then found := true) ia;
!found
let ia_get ia ann_name =
let found = ref None in
ia_iter (fun a -> if String.equal ann_name a.Annot.class_name then found := Some a) ia;
!found
let ma_contains ma ann_names =
let found = ref false in
ma_iter (fun a ->
if IList.exists (String.equal a.Annot.class_name) ann_names then found := true
) ma;
!found
let pdesc_has_annot pdesc annot =
ma_contains (Procdesc.get_attributes pdesc).ProcAttributes.method_annotation [annot]
let field_has_annot fieldname (struct_typ : StructTyp.t) f =
let fld_has_taint_annot (fname, _, annot) =
Ident.equal_fieldname fieldname fname && f annot in
IList.exists fld_has_taint_annot struct_typ.fields ||
IList.exists fld_has_taint_annot struct_typ.statics
let ia_is_not_thread_safe ia =
ia_ends_with ia not_thread_safe
let ia_is_thread_safe ia =
ia_ends_with ia thread_safe
let ia_is_thread_safe_method ia =
ia_ends_with ia thread_safe_method
let ia_is_assume_thread_safe ia =
ia_ends_with ia assume_thread_safe
let ia_is_nullable ia =
ia_ends_with ia nullable
let ia_is_present ia =
ia_ends_with ia present
let ia_is_nonnull ia =
IList.exists
(ia_ends_with ia)
[nonnull; notnull; camel_nonnull]
let ia_is_false_on_null ia =
ia_ends_with ia false_on_null
let ia_is_true_on_null ia =
ia_ends_with ia true_on_null
let ia_is_initializer ia =
ia_ends_with ia initializer_
let ia_is_volatile ia =
ia_contains ia volatile
let field_injector_readwrite_list =
[
inject_view;
bind;
bind_view;
bind_array;
bind_bitmap;
bind_drawable;
bind_string;
suppress_view_nullability;
]
let field_injector_readonly_list =
inject
::
field_injector_readwrite_list
(** Annotations for readonly injectors.
The injector framework initializes the field but does not write null into it. *)
let ia_is_field_injector_readonly ia =
IList.exists
(ia_ends_with ia)
field_injector_readonly_list
(** Annotations for read-write injectors.
The injector framework initializes the field and can write null into it. *)
let ia_is_field_injector_readwrite ia =
IList.exists
(ia_ends_with ia)
field_injector_readwrite_list
let ia_is_mutable ia =
ia_ends_with ia mutable_
let ia_get_strict ia =
ia_get ia strict
let ia_is_verify ia =
ia_contains ia verify_annotation
let ia_is_expensive ia =
ia_ends_with ia expensive
let ia_is_performance_critical ia =
ia_ends_with ia performance_critical
let ia_is_no_allocation ia =
ia_ends_with ia no_allocation
let ia_is_ignore_allocations ia =
ia_ends_with ia ignore_allocations
let ia_is_suppress_lint ia =
ia_ends_with ia suppress_lint
let ia_is_on_event ia =
ia_ends_with ia on_event
let ia_is_on_bind ia =
ia_ends_with ia on_bind
let ia_is_on_mount ia =
ia_ends_with ia on_mount
let ia_is_on_unbind ia =
ia_ends_with ia on_unbind
let ia_is_on_unmount ia =
ia_ends_with ia on_unmount
let ia_is_privacy_source ia =
ia_ends_with ia privacy_source
let ia_is_privacy_sink ia =
ia_ends_with ia privacy_sink
let ia_is_integrity_source ia =
ia_ends_with ia integrity_source
let ia_is_integrity_sink ia =
ia_ends_with ia integrity_sink
let ia_is_guarded_by ia =
ia_ends_with ia guarded_by
let ia_is_ui_thread ia =
ia_ends_with ia ui_thread
let ia_is_thread_confined ia =
ia_ends_with ia thread_confined
type annotation =
| Nullable
| Present
[@@deriving compare]
let ia_is ann ia = match ann with
| Nullable -> ia_is_nullable ia
| Present -> ia_is_present ia
(** Get a method signature with annotations from a proc_attributes. *)
let get_annotated_signature proc_attributes : annotated_signature =
let method_annotation = proc_attributes.ProcAttributes.method_annotation in
let formals = proc_attributes.ProcAttributes.formals in
let ret_type = proc_attributes.ProcAttributes.ret_type in
let (ia, ial0) = method_annotation in
let natl =
let rec extract ial parl = match ial, parl with
| ia :: ial', (name, typ) :: parl' ->
(name, ia, typ) :: extract ial' parl'
| [], (name, typ) :: parl' ->
(name, Annot.Item.empty, typ) :: extract [] parl'
| [], [] ->
[]
| _ :: _, [] ->
assert false in
IList.rev (extract (IList.rev ial0) (IList.rev formals)) in
let annotated_signature = { ret = (ia, ret_type); params = natl } in
annotated_signature
(** Check if the annotated signature is for a wrapper of an anonymous inner class method.
These wrappers have the same name as the original method, every type is Object, and the parameters
are called x0, x1, x2. *)
let annotated_signature_is_anonymous_inner_class_wrapper ann_sig proc_name =
let check_ret (ia, t) =
Annot.Item.is_empty ia && PatternMatch.type_is_object t in
let x_param_found = ref false in
let name_is_x_number name =
let name_str = Mangled.to_string name in
let len = String.length name_str in
len >= 2 &&
String.equal (String.sub name_str ~pos:0 ~len:1) "x" &&
let s = String.sub name_str ~pos:1 ~len:(len - 1) in
let is_int =
try
ignore (int_of_string s);
x_param_found := true;
true
with Failure _ -> false in
is_int in
let check_param (name, ia, t) =
if String.equal (Mangled.to_string name) "this" then true
else
name_is_x_number name &&
Annot.Item.is_empty ia &&
PatternMatch.type_is_object t in
Procname.java_is_anonymous_inner_class proc_name
&& check_ret ann_sig.ret
&& IList.for_all check_param ann_sig.params
&& !x_param_found
(** Check if the given parameter has a Nullable annotation in the given signature *)
let param_is_nullable pvar ann_sig =
IList.exists
(fun (param, annot, _) ->
Mangled.equal param (Pvar.get_name pvar) && ia_is_nullable annot)
ann_sig.params
(** Pretty print a method signature with annotations. *)
let pp_annotated_signature proc_name fmt annotated_signature =
let pp_ia fmt ia = if ia <> [] then F.fprintf fmt "%a " Annot.Item.pp ia in
let pp_annotated_param fmt (p, ia, t) =
F.fprintf fmt " %a%a %a" pp_ia ia (Typ.pp_full Pp.text) t Mangled.pp p in
let ia, ret_type = annotated_signature.ret in
F.fprintf fmt "%a%a %s (%a )"
pp_ia ia
(Typ.pp_full Pp.text) ret_type
(Procname.to_simplified_string proc_name)
(Pp.comma_seq pp_annotated_param) annotated_signature.params
let mk_ann_str s = { Annot.class_name = s; parameters = [] }
let mk_ann = function
| Nullable -> mk_ann_str nullable
| Present -> mk_ann_str present
let mk_ia ann ia =
if ia_is ann ia then ia
else (mk_ann ann, true) :: ia
let mark_ia ann ia x =
if x then mk_ia ann ia else ia
let mk_ia_strict ia =
if ia_get_strict ia <> None then ia
else (mk_ann_str strict, true) :: ia
let mark_ia_strict ia x =
if x then mk_ia_strict ia else ia
(** Mark the annotated signature with the given annotation map. *)
let annotated_signature_mark proc_name ann asig (b, bs) =
let ia, t = asig.ret in
let ret' = mark_ia ann ia b, t in
let mark_param (s, ia, t) x =
let ia' = if x then mk_ia ann ia else ia in
(s, ia', t) in
let params' =
let fail () =
L.stdout
"INTERNAL ERROR: annotation for procedure %s has wrong number of arguments@."
(Procname.to_unique_id proc_name);
L.stdout " ANNOTATED SIGNATURE: %a@." (pp_annotated_signature proc_name) asig;
assert false in
let rec combine l1 l2 = match l1, l2 with
| (p, ia, t):: l1', l2' when String.equal (Mangled.to_string p) "this" ->
(p, ia, t) :: combine l1' l2'
| (s, ia, t):: l1', x:: l2' ->
mark_param (s, ia, t) x :: combine l1' l2'
| [], _:: _ -> fail ()
| _:: _, [] -> fail ()
| [], [] -> [] in
combine asig.params bs in
{ ret = ret'; params = params'}
(** Mark the return of the annotated signature with the given annotation. *)
let annotated_signature_mark_return ann asig =
let ia, t = asig.ret in
let ret' = mark_ia ann ia true, t in
{ asig with ret = ret'}
(** Mark the return of the annotated signature @Strict. *)
let annotated_signature_mark_return_strict asig =
let ia, t = asig.ret in
let ret' = mark_ia_strict ia true, t in
{ asig with ret = ret'}
(** Mark the return of the method_annotation with the given annotation. *)
let method_annotation_mark_return ann method_annotation =
let ia_ret, params = method_annotation in
let ia_ret' = mark_ia ann ia_ret true in
ia_ret', params