diff --git a/infer/bin/inferlib.py b/infer/bin/inferlib.py index be9a81b78..aaad09d63 100644 --- a/infer/bin/inferlib.py +++ b/infer/bin/inferlib.py @@ -284,7 +284,7 @@ def should_report(analyzer, row): 'MEMORY_LEAK', 'RETAIN_CYCLE', 'ASSERTION_FAILURE', - 'ACTIVITY_LEAK', + 'CONTEXT_LEAK', 'BAD_POINTER_COMPARISON', # 'CHECKERS_PRINTF_ARGS' # TODO (#8030397): revert this once all the checkers are moved to Infer diff --git a/infer/src/backend/errdesc.ml b/infer/src/backend/errdesc.ml index 9e616de38..56fb52a04 100644 --- a/infer/src/backend/errdesc.ml +++ b/infer/src/backend/errdesc.ml @@ -26,9 +26,9 @@ let hpred_is_open_resource prop = function | _ -> None -(** Produce a description of a persistent reference to an Android Activity *) -let explain_activity_leak pname activity_typ fieldname error_path = - Localise.desc_activity_leak pname activity_typ fieldname error_path +(** Produce a description of a persistent reference to an Android Context *) +let explain_context_leak pname context_typ fieldname error_path = + Localise.desc_context_leak pname context_typ fieldname error_path (** Explain a deallocate stack variable error *) let explain_deallocate_stack_var pvar ra = diff --git a/infer/src/backend/errdesc.mli b/infer/src/backend/errdesc.mli index d275889c6..6f8f3cb76 100644 --- a/infer/src/backend/errdesc.mli +++ b/infer/src/backend/errdesc.mli @@ -40,8 +40,8 @@ val find_boolean_assignment : Cfg.Node.t -> Sil.pvar -> bool -> Cfg.Node.t optio (** describe rvalue [e] as a dexp *) val exp_rv_dexp : Cfg.Node.t -> Sil.exp -> Sil.dexp option -(** Produce a description of a persistent reference to an Android Activity *) -val explain_activity_leak : Procname.t -> Sil.typ -> Ident.fieldname -> +(** Produce a description of a persistent reference to an Android Context *) +val explain_context_leak : Procname.t -> Sil.typ -> Ident.fieldname -> (Ident.fieldname option * Sil.typ) list -> Localise.error_desc (** Produce a description of a pointer dangerously coerced to a boolean in a comparison *) diff --git a/infer/src/backend/exceptions.ml b/infer/src/backend/exceptions.ml index 61b9a020b..c4265f725 100644 --- a/infer/src/backend/exceptions.ml +++ b/infer/src/backend/exceptions.ml @@ -30,7 +30,6 @@ type err_kind = Kwarning | Kerror | Kinfo 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 Array_out_of_bounds_l1 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 Condition_is_assignment of Localise.error_desc * 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 Deallocate_stack_variable 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 *) | Abduction_case_not_implemented mloc -> (Localise.from_string "Abduction_case_not_implemented", Localise.no_desc, Some mloc, Exn_developer, Low, None, Nocat) - | Activity_leak (desc, _) -> - (Localise.activity_leak, desc, None, Exn_user, High, None, Nocat) + | Context_leak (desc, _) -> + (Localise.context_leak, desc, None, Exn_user, High, None, Nocat) | Analysis_stops (desc, mloco) -> let visibility = if !Config.analysis_stops then Exn_user else Exn_developer in (Localise.analysis_stops, desc, mloco, visibility, Medium, None, Nocat) diff --git a/infer/src/backend/exceptions.mli b/infer/src/backend/exceptions.mli index c86833fa2..c85d8105e 100644 --- a/infer/src/backend/exceptions.mli +++ b/infer/src/backend/exceptions.mli @@ -30,7 +30,6 @@ type err_kind = type err_class = Checker | Prover | Nocat 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 Array_of_pointsto of 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 Condition_always_true_false of Localise.error_desc * bool * 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 Deallocate_stack_variable of Localise.error_desc exception Deallocate_static_memory of Localise.error_desc diff --git a/infer/src/backend/interproc.ml b/infer/src/backend/interproc.ml index 4688ddad7..4990106f8 100644 --- a/infer/src/backend/interproc.ml +++ b/infer/src/backend/interproc.ml @@ -608,29 +608,30 @@ let forward_tabulate cfg tenv = done; 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 an expression in [activity_exps] is reachable from [field_strexp] *) - let check_reachable_activity_from_fld (fld_name, fld_strexp) activity_exps = + +(** report an error if any Context is reachable from a static field *) +let report_context_leaks pname sigma tenv = + (* 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 reachable_hpreds, reachable_exps = 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 - (fun (activity_exp, typ) -> - if Sil.ExpSet.mem activity_exp reachable_exps then - let leak_path = Prop.get_fld_typ_path fld_exps activity_exp reachable_hpreds in - let err_desc = Errdesc.explain_activity_leak pname typ fld_name leak_path in - let exn = Exceptions.Activity_leak + (fun (context_exp, typ) -> + if Sil.ExpSet.mem context_exp reachable_exps then + let leak_path = Prop.get_fld_typ_path fld_exps context_exp reachable_hpreds in + let err_desc = Errdesc.explain_context_leak pname typ fld_name leak_path in + let exn = Exceptions.Context_leak (err_desc, try assert false with Assert_failure x -> x) in Reporting.log_error pname exn) - activity_exps in - (* get the set of pointed-to expressions of type T <: Activity *) - let activity_exps = + context_exps in + (* get the set of pointed-to expressions of type T <: Context *) + let context_exps = IList.fold_left (fun exps hpred -> match hpred with | 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 | _ -> exps) [] @@ -641,7 +642,7 @@ let report_activity_leaks pname sigma tenv = IList.iter (fun (f_name, f_strexp) -> 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 @@ -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' = Prop.normalize (Prop.prop_sub sub pre) in 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' = if Prover.check_inconsistency_base prop then None else Some (Prop.normalize (Prop.prop_sub sub post), path) in diff --git a/infer/src/backend/localise.ml b/infer/src/backend/localise.ml index 0e938950e..319d23d81 100644 --- a/infer/src/backend/localise.ml +++ b/infer/src/backend/localise.ml @@ -28,7 +28,7 @@ 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 context_leak = "CONTEXT_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" @@ -365,7 +365,7 @@ let java_unchecked_exn_desc proc_name exn_name pre_str : error_desc = "can throw "^(Mangled.to_string exn_name); "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 leak_root = 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 (* intentionally omit space; [typ_to_string] adds an extra space *) 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_prefix = if leak_path = [] then "leaked " 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 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, []) let desc_assertion_failure loc : error_desc = diff --git a/infer/src/backend/localise.mli b/infer/src/backend/localise.mli index d1e76aa7b..55abc9103 100644 --- a/infer/src/backend/localise.mli +++ b/infer/src/backend/localise.mli @@ -25,7 +25,7 @@ val to_string : t -> string (** compare two localised strings *) val compare : t -> t -> int -val activity_leak : t +val context_leak : t val analysis_stops : t val array_out_of_bounds_l1 : 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 desc_activity_leak : Procname.t -> Sil.typ -> Ident.fieldname -> - (Ident.fieldname option * Sil.typ) list -> error_desc +val desc_context_leak : + Procname.t -> Sil.typ -> Ident.fieldname -> (Ident.fieldname option * Sil.typ) list -> error_desc (* Create human-readable error description for assertion failures *) val desc_assertion_failure : Location.t -> error_desc diff --git a/infer/src/harness/androidFramework.ml b/infer/src/harness/androidFramework.ml index 58c51658a..f207b2ac4 100644 --- a/infer/src/harness/androidFramework.ml +++ b/infer/src/harness/androidFramework.ml @@ -267,12 +267,15 @@ let get_all_supertypes typ tenv = let is_subtype (typ0 : Sil.typ) (typ1 : Sil.typ) tenv = TypSet.mem typ1 (get_all_supertypes typ0 tenv) -(** return true if [typ] <: android.app.Activity *) -let is_activity typ tenv = - let activity_mangled = Mangled.from_package_class "android.app" "Activity" in - match Sil.get_typ activity_mangled (Some Sil.Class) tenv with - | Some activity_typ -> is_subtype typ activity_typ tenv - | None -> false +(** return true if [typ] <: android.content.Context *) +let is_context typ tenv = + let context_classname = Mangled.from_package_class "android.content" "Context" + and application_classname = Mangled.from_package_class "android.app" "Application" in + let subclass_of classname = + 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 *) let is_callback_class_name class_name = Mangled.MangledSet.mem class_name android_callbacks diff --git a/infer/src/harness/androidFramework.mli b/infer/src/harness/androidFramework.mli index 99bece57b..351365068 100644 --- a/infer/src/harness/androidFramework.mli +++ b/infer/src/harness/androidFramework.mli @@ -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 *) val is_callback_class : Sil.typ -> Sil.tenv -> bool -(** return true if [typ] <: android.app.Activity *) -val is_activity : Sil.typ -> Sil.tenv -> bool +(** return true if [typ] <: android.content.Context *) +val is_context : Sil.typ -> Sil.tenv -> bool (** return true if [procname] is a special lifecycle cleanup method *) val is_destroy_method : Procname.t -> bool diff --git a/infer/tests/ant_report.json b/infer/tests/ant_report.json index f06f3a304..ea74a5d09 100644 --- a/infer/tests/ant_report.json +++ b/infer/tests/ant_report.json @@ -1,35 +1,35 @@ [ { - "line": "22", - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.directLeak()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "line": "23", + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.directLeak()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { - "line": "41", - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.indirectLeak()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "line": "42", + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.indirectLeak()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.nonStaticInnerClassLeak()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.nonStaticInnerClassLeak()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.leakAfterInstanceFieldWrite()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.leakAfterInstanceFieldWrite()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "ActivityLeaks$Singleton ActivityLeaks.singletonLeak()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "ContextLeaks$Singleton ContextLeaks.singletonLeak()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.handlerLeak()", - "file": "codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.handlerLeak()", + "file": "codetoanalyze/java/infer/ContextLeaks.java" }, { "type": "NULL_DEREFERENCE", diff --git a/infer/tests/buck_report.json b/infer/tests/buck_report.json index 5ebd7c5b6..6e2b61632 100644 --- a/infer/tests/buck_report.json +++ b/infer/tests/buck_report.json @@ -1,33 +1,33 @@ [ { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.directLeak()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.directLeak()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.indirectLeak()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.indirectLeak()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.nonStaticInnerClassLeak()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.nonStaticInnerClassLeak()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.leakAfterInstanceFieldWrite()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.leakAfterInstanceFieldWrite()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "ActivityLeaks$Singleton ActivityLeaks.singletonLeak()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "ContextLeaks$Singleton ContextLeaks.singletonLeak()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { - "type": "ACTIVITY_LEAK", - "procedure": "void ActivityLeaks.handlerLeak()", - "file": "infer/tests/codetoanalyze/java/infer/ActivityLeaks.java" + "type": "CONTEXT_LEAK", + "procedure": "void ContextLeaks.handlerLeak()", + "file": "infer/tests/codetoanalyze/java/infer/ContextLeaks.java" }, { "type": "NULL_DEREFERENCE", diff --git a/infer/tests/codetoanalyze/java/infer/ActivityLeaks.java b/infer/tests/codetoanalyze/java/infer/ContextLeaks.java similarity index 98% rename from infer/tests/codetoanalyze/java/infer/ActivityLeaks.java rename to infer/tests/codetoanalyze/java/infer/ContextLeaks.java index a9e0da256..521254461 100644 --- a/infer/tests/codetoanalyze/java/infer/ActivityLeaks.java +++ b/infer/tests/codetoanalyze/java/infer/ContextLeaks.java @@ -9,11 +9,12 @@ package codetoanalyze.java.infer; -import android.app.Activity; import android.content.Context; +import android.app.Activity; import android.os.Handler; -public class ActivityLeaks extends Activity { + +public class ContextLeaks extends Activity { static Object sFld; diff --git a/infer/tests/endtoend/java/infer/ActivityLeaksTest.java b/infer/tests/endtoend/java/infer/ContextLeaksTest.java similarity index 82% rename from infer/tests/endtoend/java/infer/ActivityLeaksTest.java rename to infer/tests/endtoend/java/infer/ContextLeaksTest.java index ebd6e5f9e..c11be06fe 100644 --- a/infer/tests/endtoend/java/infer/ActivityLeaksTest.java +++ b/infer/tests/endtoend/java/infer/ContextLeaksTest.java @@ -20,19 +20,19 @@ import java.io.IOException; import utils.InferException; import utils.InferResults; -public class ActivityLeaksTest { +public class ContextLeaksTest { 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; @BeforeClass public static void loadResults() throws InterruptedException, IOException { inferResults = InferResults.loadInferResults( - ActivityLeaksTest.class, + ContextLeaksTest.class, SOURCE_FILE); } @@ -48,10 +48,10 @@ public class ActivityLeaksTest { "handlerLeak" }; assertThat( - "Results should contain " + ACTIVITY_LEAK, + "Results should contain " + CONTEXT_LEAK, inferResults, containsExactly( - ACTIVITY_LEAK, + CONTEXT_LEAK, SOURCE_FILE, methods )