|
|
|
(*
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** Make sure callbacks are always unregistered. drive the point home by reporting possible NPE's *)
|
|
|
|
|
|
|
|
module L = Logging
|
|
|
|
module F = Format
|
|
|
|
module P = Printf
|
|
|
|
module IdSet = Ident.IdentSet
|
|
|
|
module FldSet = Ident.FieldSet
|
|
|
|
open Utils
|
|
|
|
|
|
|
|
(** Automatically create a harness method to exercise code under test *)
|
|
|
|
|
|
|
|
(** return the set of instance fields that are assigned to a null literal in [procdesc] *)
|
|
|
|
let get_fields_nullified procdesc =
|
|
|
|
(* walk through the instructions and look for instance fields that are assigned to null *)
|
|
|
|
let collect_nullified_flds (nullified_flds, this_ids) _ = function
|
|
|
|
| Sil.Set (Sil.Lfield (Sil.Var lhs, fld, _), typ, rhs, loc)
|
|
|
|
when Sil.exp_is_null_literal rhs && IdSet.mem lhs this_ids ->
|
|
|
|
(FldSet.add fld nullified_flds, this_ids)
|
|
|
|
| Sil.Letderef (id, rhs, _, _) when Sil.exp_is_this rhs ->
|
|
|
|
(nullified_flds, IdSet.add id this_ids)
|
|
|
|
| _ -> (nullified_flds, this_ids) in
|
|
|
|
let (nullified_flds, _) =
|
|
|
|
Cfg.Procdesc.fold_instrs collect_nullified_flds (FldSet.empty, IdSet.empty) procdesc in
|
|
|
|
nullified_flds
|
|
|
|
|
|
|
|
(** set of instance fields belonging to the current file that are assigned to null literals *)
|
|
|
|
let fields_nullified = ref FldSet.empty
|
|
|
|
|
|
|
|
(** set of callbacks registered in the current file *)
|
|
|
|
let registered_callback_procs = ref Procname.Set.empty
|
|
|
|
|
|
|
|
let android_lifecycle_typs = ref []
|
|
|
|
|
|
|
|
(** resolve the list of android lifecycle type strings in [tenv] *)
|
|
|
|
let get_or_create_lifecycle_typs tenv = match !android_lifecycle_typs with
|
|
|
|
| [] ->
|
|
|
|
let lifecycle_typs = list_fold_left (fun typs (pkg, clazz, methods) ->
|
|
|
|
let qualified_name = Mangled.from_package_class pkg clazz in
|
|
|
|
match AndroidFramework.get_lifecycle_for_framework_typ_opt
|
|
|
|
qualified_name methods tenv with
|
|
|
|
| Some (framework_typ, _) -> framework_typ :: typs
|
|
|
|
| None -> typs
|
|
|
|
) [] AndroidFramework.get_lifecycles in
|
|
|
|
android_lifecycle_typs := lifecycle_typs;
|
|
|
|
lifecycle_typs
|
|
|
|
| typs -> typs
|
|
|
|
|
|
|
|
let do_eradicate_check all_procs get_procdesc idenv tenv proc_name proc_desc =
|
|
|
|
Eradicate.callback_eradicate all_procs get_procdesc idenv tenv proc_name proc_desc
|
|
|
|
|
|
|
|
let num_methods_checked = ref 0
|
|
|
|
|
|
|
|
let done_checking num_methods =
|
|
|
|
incr num_methods_checked;
|
|
|
|
!num_methods_checked = num_methods
|
|
|
|
|
|
|
|
(** ask Eradicate to check each of the procs in [registered_callback_procs] (and their transitive
|
|
|
|
* callees) in a context where each of the fields in [fields_nullifed] is marked as @Nullable *)
|
|
|
|
let do_eradicate_check all_procs get_procdesc idenv tenv =
|
|
|
|
(* tell Eradicate to treat each of the fields nullified in on_destroy as nullable *)
|
|
|
|
FldSet.iter (fun fld -> Models.Inference.field_add_nullable_annotation fld) !fields_nullified;
|
|
|
|
Procname.Set.iter
|
|
|
|
(fun proc_name ->
|
|
|
|
match get_procdesc proc_name with
|
|
|
|
| Some proc_desc ->
|
|
|
|
do_eradicate_check all_procs get_procdesc idenv tenv proc_name proc_desc
|
|
|
|
| None -> ())
|
|
|
|
!registered_callback_procs
|
|
|
|
|
|
|
|
(** if [procname] belongs to an Android lifecycle type, save the set of callbacks registered in
|
|
|
|
* [procname]. in addition, if [procname] is a special "destroy" /"cleanup" method, save the set of
|
|
|
|
* fields that are nullified *)
|
|
|
|
let callback_checker_main all_procs get_procdesc idenv tenv proc_name proc_desc =
|
|
|
|
match Sil.get_typ (Mangled.from_string (Procname.java_get_class proc_name)) None tenv with
|
|
|
|
| Some (Sil.Tstruct(_, _, csu, Some class_name, _, methods, _) as typ) ->
|
|
|
|
let lifecycle_typs = get_or_create_lifecycle_typs tenv in
|
|
|
|
let proc_belongs_to_lifecycle_typ = list_exists
|
|
|
|
(fun lifecycle_typ -> AndroidFramework.typ_is_lifecycle_typ typ lifecycle_typ tenv)
|
|
|
|
lifecycle_typs in
|
|
|
|
if proc_belongs_to_lifecycle_typ then
|
|
|
|
(* TODO (tt4959422): get all of the callbacks registered by callees as well *)
|
|
|
|
let registered_callback_typs =
|
|
|
|
AndroidFramework.get_callbacks_registered_by_proc proc_desc tenv in
|
|
|
|
(* find the callbacks registered by this procedure and update the list *)
|
|
|
|
let registered_callback_procs' = list_fold_left
|
|
|
|
(fun callback_procs callback_typ ->
|
|
|
|
match callback_typ with
|
|
|
|
| Sil.Tptr (Sil.Tstruct(_, _, Sil.Class, Some class_name, _, methods, _), _) ->
|
|
|
|
list_fold_left
|
|
|
|
(fun callback_procs callback_proc ->
|
|
|
|
if Procname.is_constructor callback_proc then callback_procs
|
|
|
|
else Procname.Set.add callback_proc callback_procs)
|
|
|
|
callback_procs
|
|
|
|
methods
|
|
|
|
| typ -> callback_procs)
|
|
|
|
!registered_callback_procs
|
|
|
|
registered_callback_typs in
|
|
|
|
registered_callback_procs := registered_callback_procs';
|
|
|
|
let _ = if AndroidFramework.is_destroy_method proc_name then
|
|
|
|
(* compute the set of fields nullified by this procedure *)
|
|
|
|
(* TODO (t4959422): get fields that are nullified in callees of the destroy method *)
|
|
|
|
fields_nullified := FldSet.union (get_fields_nullified proc_desc) !fields_nullified in
|
|
|
|
if done_checking (list_length methods) then
|
|
|
|
do_eradicate_check all_procs get_procdesc idenv tenv
|
|
|
|
| _ -> ()
|