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.

115 lines
5.4 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.
*)
(** 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
| _ -> ()