report leaks on all context, not only activities

Summary: public
Extends the current activity leak checker to all sort of context leaks.

Reviewed By: sblackshear

Differential Revision: D2572548

fb-gh-sync-id: 9da18e4
master
jrm 9 years ago committed by facebook-github-bot-7
parent 197b13434a
commit d8e74e456c

@ -284,7 +284,7 @@ def should_report(analyzer, row):
'MEMORY_LEAK', 'MEMORY_LEAK',
'RETAIN_CYCLE', 'RETAIN_CYCLE',
'ASSERTION_FAILURE', 'ASSERTION_FAILURE',
'ACTIVITY_LEAK', 'CONTEXT_LEAK',
'BAD_POINTER_COMPARISON', 'BAD_POINTER_COMPARISON',
# 'CHECKERS_PRINTF_ARGS' # 'CHECKERS_PRINTF_ARGS'
# TODO (#8030397): revert this once all the checkers are moved to Infer # TODO (#8030397): revert this once all the checkers are moved to Infer

@ -26,9 +26,9 @@ let hpred_is_open_resource prop = function
| _ -> | _ ->
None None
(** Produce a description of a persistent reference to an Android Activity *) (** Produce a description of a persistent reference to an Android Context *)
let explain_activity_leak pname activity_typ fieldname error_path = let explain_context_leak pname context_typ fieldname error_path =
Localise.desc_activity_leak pname activity_typ fieldname error_path Localise.desc_context_leak pname context_typ fieldname error_path
(** Explain a deallocate stack variable error *) (** Explain a deallocate stack variable error *)
let explain_deallocate_stack_var pvar ra = let explain_deallocate_stack_var pvar ra =

@ -40,8 +40,8 @@ val find_boolean_assignment : Cfg.Node.t -> Sil.pvar -> bool -> Cfg.Node.t optio
(** describe rvalue [e] as a dexp *) (** describe rvalue [e] as a dexp *)
val exp_rv_dexp : Cfg.Node.t -> Sil.exp -> Sil.dexp option val exp_rv_dexp : Cfg.Node.t -> Sil.exp -> Sil.dexp option
(** Produce a description of a persistent reference to an Android Activity *) (** Produce a description of a persistent reference to an Android Context *)
val explain_activity_leak : Procname.t -> Sil.typ -> Ident.fieldname -> val explain_context_leak : Procname.t -> Sil.typ -> Ident.fieldname ->
(Ident.fieldname option * Sil.typ) list -> Localise.error_desc (Ident.fieldname option * Sil.typ) list -> Localise.error_desc
(** Produce a description of a pointer dangerously coerced to a boolean in a comparison *) (** Produce a description of a pointer dangerously coerced to a boolean in a comparison *)

@ -30,7 +30,6 @@ type err_kind =
Kwarning | Kerror | Kinfo Kwarning | Kerror | Kinfo
exception Abduction_case_not_implemented of ml_location exception Abduction_case_not_implemented of ml_location
exception Activity_leak of Localise.error_desc * ml_location
exception Analysis_stops of Localise.error_desc * ml_location option exception Analysis_stops of Localise.error_desc * ml_location option
exception Array_out_of_bounds_l1 of Localise.error_desc * ml_location exception Array_out_of_bounds_l1 of Localise.error_desc * ml_location
exception Array_out_of_bounds_l2 of Localise.error_desc * ml_location exception Array_out_of_bounds_l2 of Localise.error_desc * ml_location
@ -44,6 +43,7 @@ exception Codequery of Localise.error_desc
exception Comparing_floats_for_equality of Localise.error_desc * ml_location exception Comparing_floats_for_equality of Localise.error_desc * ml_location
exception Condition_is_assignment of Localise.error_desc * ml_location exception Condition_is_assignment of Localise.error_desc * ml_location
exception Condition_always_true_false of Localise.error_desc * bool * ml_location exception Condition_always_true_false of Localise.error_desc * bool * ml_location
exception Context_leak of Localise.error_desc * ml_location
exception Dangling_pointer_dereference of Sil.dangling_kind option * Localise.error_desc * ml_location exception Dangling_pointer_dereference of Sil.dangling_kind option * Localise.error_desc * ml_location
exception Deallocate_stack_variable of Localise.error_desc exception Deallocate_stack_variable of Localise.error_desc
exception Deallocate_static_memory of Localise.error_desc exception Deallocate_static_memory of Localise.error_desc
@ -89,8 +89,8 @@ let recognize_exception exn =
let err_name, desc, mloco, visibility, severity, force_kind, eclass = match exn with (* all the names of Exn_user errors must be defined in Localise *) let err_name, desc, mloco, visibility, severity, force_kind, eclass = match exn with (* all the names of Exn_user errors must be defined in Localise *)
| Abduction_case_not_implemented mloc -> | Abduction_case_not_implemented mloc ->
(Localise.from_string "Abduction_case_not_implemented", Localise.no_desc, Some mloc, Exn_developer, Low, None, Nocat) (Localise.from_string "Abduction_case_not_implemented", Localise.no_desc, Some mloc, Exn_developer, Low, None, Nocat)
| Activity_leak (desc, _) -> | Context_leak (desc, _) ->
(Localise.activity_leak, desc, None, Exn_user, High, None, Nocat) (Localise.context_leak, desc, None, Exn_user, High, None, Nocat)
| Analysis_stops (desc, mloco) -> | Analysis_stops (desc, mloco) ->
let visibility = if !Config.analysis_stops then Exn_user else Exn_developer in let visibility = if !Config.analysis_stops then Exn_user else Exn_developer in
(Localise.analysis_stops, desc, mloco, visibility, Medium, None, Nocat) (Localise.analysis_stops, desc, mloco, visibility, Medium, None, Nocat)

@ -30,7 +30,6 @@ type err_kind =
type err_class = Checker | Prover | Nocat type err_class = Checker | Prover | Nocat
exception Abduction_case_not_implemented of ml_location exception Abduction_case_not_implemented of ml_location
exception Activity_leak of Localise.error_desc * ml_location
exception Analysis_stops of Localise.error_desc * ml_location option exception Analysis_stops of Localise.error_desc * ml_location option
exception Array_of_pointsto of ml_location exception Array_of_pointsto of ml_location
exception Array_out_of_bounds_l1 of Localise.error_desc * ml_location exception Array_out_of_bounds_l1 of Localise.error_desc * ml_location
@ -44,6 +43,7 @@ exception Codequery of Localise.error_desc
exception Comparing_floats_for_equality of Localise.error_desc * ml_location exception Comparing_floats_for_equality of Localise.error_desc * ml_location
exception Condition_always_true_false of Localise.error_desc * bool * ml_location exception Condition_always_true_false of Localise.error_desc * bool * ml_location
exception Condition_is_assignment of Localise.error_desc * ml_location exception Condition_is_assignment of Localise.error_desc * ml_location
exception Context_leak of Localise.error_desc * ml_location
exception Dangling_pointer_dereference of Sil.dangling_kind option * Localise.error_desc * ml_location exception Dangling_pointer_dereference of Sil.dangling_kind option * Localise.error_desc * ml_location
exception Deallocate_stack_variable of Localise.error_desc exception Deallocate_stack_variable of Localise.error_desc
exception Deallocate_static_memory of Localise.error_desc exception Deallocate_static_memory of Localise.error_desc

@ -608,29 +608,30 @@ let forward_tabulate cfg tenv =
done; done;
L.d_strln ".... Work list empty. Stop ...."; L.d_ln () L.d_strln ".... Work list empty. Stop ...."; L.d_ln ()
(** report an error if any Activity is reachable from a static field *)
let report_activity_leaks pname sigma tenv = (** report an error if any Context is reachable from a static field *)
(* report an error if an expression in [activity_exps] is reachable from [field_strexp] *) let report_context_leaks pname sigma tenv =
let check_reachable_activity_from_fld (fld_name, fld_strexp) activity_exps = (* report an error if an expression in [context_exps] is reachable from [field_strexp] *)
let check_reachable_context_from_fld (fld_name, fld_strexp) context_exps =
let fld_exps = Prop.strexp_get_exps fld_strexp in let fld_exps = Prop.strexp_get_exps fld_strexp in
let reachable_hpreds, reachable_exps = let reachable_hpreds, reachable_exps =
Prop.compute_reachable_hpreds sigma fld_exps in Prop.compute_reachable_hpreds sigma fld_exps in
(* raise an error if any Activity expression is in [reachable_exps] *) (* raise an error if any Context expression is in [reachable_exps] *)
IList.iter IList.iter
(fun (activity_exp, typ) -> (fun (context_exp, typ) ->
if Sil.ExpSet.mem activity_exp reachable_exps then if Sil.ExpSet.mem context_exp reachable_exps then
let leak_path = Prop.get_fld_typ_path fld_exps activity_exp reachable_hpreds in let leak_path = Prop.get_fld_typ_path fld_exps context_exp reachable_hpreds in
let err_desc = Errdesc.explain_activity_leak pname typ fld_name leak_path in let err_desc = Errdesc.explain_context_leak pname typ fld_name leak_path in
let exn = Exceptions.Activity_leak let exn = Exceptions.Context_leak
(err_desc, try assert false with Assert_failure x -> x) in (err_desc, try assert false with Assert_failure x -> x) in
Reporting.log_error pname exn) Reporting.log_error pname exn)
activity_exps in context_exps in
(* get the set of pointed-to expressions of type T <: Activity *) (* get the set of pointed-to expressions of type T <: Context *)
let activity_exps = let context_exps =
IList.fold_left IList.fold_left
(fun exps hpred -> match hpred with (fun exps hpred -> match hpred with
| Sil.Hpointsto (_, Sil.Eexp (exp, _), Sil.Sizeof (Sil.Tptr (typ, _), _)) | Sil.Hpointsto (_, Sil.Eexp (exp, _), Sil.Sizeof (Sil.Tptr (typ, _), _))
when AndroidFramework.is_activity typ tenv -> when AndroidFramework.is_context typ tenv ->
(exp, typ) :: exps (exp, typ) :: exps
| _ -> exps) | _ -> exps)
[] []
@ -641,7 +642,7 @@ let report_activity_leaks pname sigma tenv =
IList.iter IList.iter
(fun (f_name, f_strexp) -> (fun (f_name, f_strexp) ->
if not (Harness.is_generated_field f_name) then if not (Harness.is_generated_field f_name) then
check_reachable_activity_from_fld (f_name, f_strexp) activity_exps) static_flds check_reachable_context_from_fld (f_name, f_strexp) context_exps) static_flds
| _ -> ()) | _ -> ())
sigma sigma
@ -702,7 +703,7 @@ let extract_specs tenv pdesc pathset : Prop.normal Specs.spec list =
let pre, post = Prop.extract_spec prop'' in let pre, post = Prop.extract_spec prop'' in
let pre' = Prop.normalize (Prop.prop_sub sub pre) in let pre' = Prop.normalize (Prop.prop_sub sub pre) in
if !Config.curr_language = Config.Java then if !Config.curr_language = Config.Java then
report_activity_leaks pname (Prop.get_sigma post) tenv; report_context_leaks pname (Prop.get_sigma post) tenv;
let post' = let post' =
if Prover.check_inconsistency_base prop then None if Prover.check_inconsistency_base prop then None
else Some (Prop.normalize (Prop.prop_sub sub post), path) in else Some (Prop.normalize (Prop.prop_sub sub post), path) in

@ -28,7 +28,7 @@ let to_string s = s
(** compare two localised strings *) (** compare two localised strings *)
let compare (s1: string) (s2: string) = Pervasives.compare s1 s2 let compare (s1: string) (s2: string) = Pervasives.compare s1 s2
let activity_leak = "ACTIVITY_LEAK" let context_leak = "CONTEXT_LEAK"
let analysis_stops = "ANALYSIS_STOPS" let analysis_stops = "ANALYSIS_STOPS"
let array_out_of_bounds_l1 = "ARRAY_OUT_OF_BOUNDS_L1" 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_l2 = "ARRAY_OUT_OF_BOUNDS_L2"
@ -365,7 +365,7 @@ let java_unchecked_exn_desc proc_name exn_name pre_str : error_desc =
"can throw "^(Mangled.to_string exn_name); "can throw "^(Mangled.to_string exn_name);
"whenever "^pre_str], None, []) "whenever "^pre_str], None, [])
let desc_activity_leak pname activity_typ fieldname leak_path : error_desc = let desc_context_leak pname context_typ fieldname leak_path : error_desc =
let fld_str = Ident.fieldname_to_string fieldname in let fld_str = Ident.fieldname_to_string fieldname in
let leak_root = let leak_root =
if fld_str = "android.os.Handler.sFakeHandlerQueue" if fld_str = "android.os.Handler.sFakeHandlerQueue"
@ -377,15 +377,15 @@ let desc_activity_leak pname activity_typ fieldname leak_path : error_desc =
| (None, typ) -> Sil.typ_to_string typ in | (None, typ) -> Sil.typ_to_string typ in
(* intentionally omit space; [typ_to_string] adds an extra space *) (* intentionally omit space; [typ_to_string] adds an extra space *)
acc ^ entry_str ^ " |->\n " in acc ^ entry_str ^ " |->\n " in
let activity_str = Sil.typ_to_string activity_typ in let context_str = Sil.typ_to_string context_typ in
let path_str = let path_str =
let path_prefix = let path_prefix =
if leak_path = [] then "leaked " if leak_path = [] then "leaked "
else (IList.fold_left leak_path_entry_to_str "" leak_path) ^ " leaked " in else (IList.fold_left leak_path_entry_to_str "" leak_path) ^ " leaked " in
path_prefix ^ activity_str in path_prefix ^ context_str in
let preamble = let preamble =
let pname_str = Procname.java_get_class pname ^ "." ^ Procname.java_get_method pname in let pname_str = Procname.java_get_class pname ^ "." ^ Procname.java_get_method pname in
"Activity " ^ activity_str ^ "may leak during method" ^ pname_str ^ ":\n" in "Context " ^ context_str ^ "may leak during method" ^ pname_str ^ ":\n" in
([preamble; leak_root; path_str], None, []) ([preamble; leak_root; path_str], None, [])
let desc_assertion_failure loc : error_desc = let desc_assertion_failure loc : error_desc =

@ -25,7 +25,7 @@ val to_string : t -> string
(** compare two localised strings *) (** compare two localised strings *)
val compare : t -> t -> int val compare : t -> t -> int
val activity_leak : t val context_leak : t
val analysis_stops : t val analysis_stops : t
val array_out_of_bounds_l1 : t val array_out_of_bounds_l1 : t
val array_out_of_bounds_l2 : t val array_out_of_bounds_l2 : t
@ -195,8 +195,8 @@ val desc_null_test_after_dereference : string -> int -> Location.t -> error_desc
val java_unchecked_exn_desc : Procname.t -> Mangled.t -> string -> error_desc val java_unchecked_exn_desc : Procname.t -> Mangled.t -> string -> error_desc
val desc_activity_leak : Procname.t -> Sil.typ -> Ident.fieldname -> val desc_context_leak :
(Ident.fieldname option * Sil.typ) list -> error_desc Procname.t -> Sil.typ -> Ident.fieldname -> (Ident.fieldname option * Sil.typ) list -> error_desc
(* Create human-readable error description for assertion failures *) (* Create human-readable error description for assertion failures *)
val desc_assertion_failure : Location.t -> error_desc val desc_assertion_failure : Location.t -> error_desc

@ -267,12 +267,15 @@ let get_all_supertypes typ tenv =
let is_subtype (typ0 : Sil.typ) (typ1 : Sil.typ) tenv = let is_subtype (typ0 : Sil.typ) (typ1 : Sil.typ) tenv =
TypSet.mem typ1 (get_all_supertypes typ0 tenv) TypSet.mem typ1 (get_all_supertypes typ0 tenv)
(** return true if [typ] <: android.app.Activity *) (** return true if [typ] <: android.content.Context *)
let is_activity typ tenv = let is_context typ tenv =
let activity_mangled = Mangled.from_package_class "android.app" "Activity" in let context_classname = Mangled.from_package_class "android.content" "Context"
match Sil.get_typ activity_mangled (Some Sil.Class) tenv with and application_classname = Mangled.from_package_class "android.app" "Application" in
| Some activity_typ -> is_subtype typ activity_typ tenv let subclass_of classname =
| None -> false match Sil.get_typ classname (Some Sil.Class) tenv with
| Some class_typ -> is_subtype typ class_typ tenv
| None -> false in
subclass_of context_classname && not (subclass_of application_classname)
(** return true if [class_name] is a known callback class name *) (** return true if [class_name] is a known callback class name *)
let is_callback_class_name class_name = Mangled.MangledSet.mem class_name android_callbacks let is_callback_class_name class_name = Mangled.MangledSet.mem class_name android_callbacks

@ -20,8 +20,8 @@ val typ_is_lifecycle_typ : Sil.typ -> Sil.typ -> Sil.tenv -> bool
(** return true if [typ] is a known callback class, false otherwise *) (** return true if [typ] is a known callback class, false otherwise *)
val is_callback_class : Sil.typ -> Sil.tenv -> bool val is_callback_class : Sil.typ -> Sil.tenv -> bool
(** return true if [typ] <: android.app.Activity *) (** return true if [typ] <: android.content.Context *)
val is_activity : Sil.typ -> Sil.tenv -> bool val is_context : Sil.typ -> Sil.tenv -> bool
(** return true if [procname] is a special lifecycle cleanup method *) (** return true if [procname] is a special lifecycle cleanup method *)
val is_destroy_method : Procname.t -> bool val is_destroy_method : Procname.t -> bool

@ -1,35 +1,35 @@
[ [
{ {
"line": "22", "line": "23",
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.directLeak()", "procedure": "void ContextLeaks.directLeak()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"line": "41", "line": "42",
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.indirectLeak()", "procedure": "void ContextLeaks.indirectLeak()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.nonStaticInnerClassLeak()", "procedure": "void ContextLeaks.nonStaticInnerClassLeak()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.leakAfterInstanceFieldWrite()", "procedure": "void ContextLeaks.leakAfterInstanceFieldWrite()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "ActivityLeaks$Singleton ActivityLeaks.singletonLeak()", "procedure": "ContextLeaks$Singleton ContextLeaks.singletonLeak()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.handlerLeak()", "procedure": "void ContextLeaks.handlerLeak()",
"file": "codetoanalyze/java/infer/ActivityLeaks.java" "file": "codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "NULL_DEREFERENCE", "type": "NULL_DEREFERENCE",

@ -1,33 +1,33 @@
[ [
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.directLeak()", "procedure": "void ContextLeaks.directLeak()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.indirectLeak()", "procedure": "void ContextLeaks.indirectLeak()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.nonStaticInnerClassLeak()", "procedure": "void ContextLeaks.nonStaticInnerClassLeak()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.leakAfterInstanceFieldWrite()", "procedure": "void ContextLeaks.leakAfterInstanceFieldWrite()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "ActivityLeaks$Singleton ActivityLeaks.singletonLeak()", "procedure": "ContextLeaks$Singleton ContextLeaks.singletonLeak()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "ACTIVITY_LEAK", "type": "CONTEXT_LEAK",
"procedure": "void ActivityLeaks.handlerLeak()", "procedure": "void ContextLeaks.handlerLeak()",
"file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java"
}, },
{ {
"type": "NULL_DEREFERENCE", "type": "NULL_DEREFERENCE",

@ -9,11 +9,12 @@
package codetoanalyze.java.infer; package codetoanalyze.java.infer;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.app.Activity;
import android.os.Handler; import android.os.Handler;
public class ActivityLeaks extends Activity {
public class ContextLeaks extends Activity {
static Object sFld; static Object sFld;

@ -20,19 +20,19 @@ import java.io.IOException;
import utils.InferException; import utils.InferException;
import utils.InferResults; import utils.InferResults;
public class ActivityLeaksTest { public class ContextLeaksTest {
public static final String SOURCE_FILE = public static final String SOURCE_FILE =
"infer/tests/codetoanalyze/java/infer/ActivityLeaks.java"; "infer/tests/codetoanalyze/java/infer/ContextLeaks.java";
public static final String ACTIVITY_LEAK = "ACTIVITY_LEAK"; public static final String CONTEXT_LEAK = "CONTEXT_LEAK";
private static InferResults inferResults; private static InferResults inferResults;
@BeforeClass @BeforeClass
public static void loadResults() throws InterruptedException, IOException { public static void loadResults() throws InterruptedException, IOException {
inferResults = InferResults.loadInferResults( inferResults = InferResults.loadInferResults(
ActivityLeaksTest.class, ContextLeaksTest.class,
SOURCE_FILE); SOURCE_FILE);
} }
@ -48,10 +48,10 @@ public class ActivityLeaksTest {
"handlerLeak" "handlerLeak"
}; };
assertThat( assertThat(
"Results should contain " + ACTIVITY_LEAK, "Results should contain " + CONTEXT_LEAK,
inferResults, inferResults,
containsExactly( containsExactly(
ACTIVITY_LEAK, CONTEXT_LEAK,
SOURCE_FILE, SOURCE_FILE,
methods methods
) )
Loading…
Cancel
Save