diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index f67a74922..8148f66ab 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -1412,10 +1412,9 @@ INTERNAL OPTIONS Specify combinations of class/method list that should be skipped during starvation analysis (default: []) - --starvation-strict-mode - Activates: During starvation analysis, report strict mode - violations (Android only) (Conversely: - --no-starvation-strict-mode) + --no-starvation-strict-mode + Deactivates: During starvation analysis, report strict mode + violations (Android only) (Conversely: --starvation-strict-mode) --stats-report file Write a report of the analysis results to a file diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index b4a73ae24..64e3d1489 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -2075,7 +2075,7 @@ and profiler_samples = and starvation_strict_mode = - CLOpt.mk_bool ~long:"starvation-strict-mode" ~default:false + CLOpt.mk_bool ~long:"starvation-strict-mode" ~default:true "During starvation analysis, report strict mode violations (Android only)" diff --git a/infer/src/concurrency/ConcurrencyModels.ml b/infer/src/concurrency/ConcurrencyModels.ml index f55746159..47864940a 100644 --- a/infer/src/concurrency/ConcurrencyModels.ml +++ b/infer/src/concurrency/ConcurrencyModels.ml @@ -160,7 +160,6 @@ let find_annotated_or_overriden_annotated_method is_annot pname tenv = let ui_matcher_records = let open MethodMatcher in - let superclasses = {empty with search_superclasses= Some true} in let fragment_methods = (* sort police: this is in lifecycle order *) [ "onAttach" @@ -175,21 +174,22 @@ let ui_matcher_records = ; "onDestroy" ; "onDetach" ] in - [ {superclasses with classname= "android.support.v4.app.Fragment"; methods= fragment_methods} - ; {superclasses with classname= "android.app.Fragment"; methods= fragment_methods} - ; {superclasses with classname= "android.content.ContentProvider"; methods= ["onCreate"]} - ; {superclasses with classname= "android.content.BroadcastReceiver"; methods= ["onReceive"]} - ; { superclasses with + (* search_superclasses is true by default in how [default] is treated *) + [ {default with classname= "android.support.v4.app.Fragment"; methods= fragment_methods} + ; {default with classname= "android.app.Fragment"; methods= fragment_methods} + ; {default with classname= "android.content.ContentProvider"; methods= ["onCreate"]} + ; {default with classname= "android.content.BroadcastReceiver"; methods= ["onReceive"]} + ; { default with classname= "android.app.Service" ; methods= ["onBind"; "onCreate"; "onDestroy"; "onStartCommand"] } - ; { superclasses with + ; { default with classname= "android.app.Activity" ; methods= ["onCreate"; "onStart"; "onRestart"; "onResume"; "onPause"; "onStop"; "onDestroy"] } - ; { superclasses with + ; { default with (* according to Android documentation, *all* methods of the View class run on UI thread, but - let's be a bit conservative and catch all methods that start with "on". - https://developer.android.com/reference/android/view/View.html *) + let's be a bit conservative and catch all methods that start with "on". + https://developer.android.com/reference/android/view/View.html *) method_prefix= Some true ; classname= "android.view.View" ; methods= ["on"] } ] diff --git a/infer/src/concurrency/MethodMatcher.ml b/infer/src/concurrency/MethodMatcher.ml index 58afe15f1..449acecd6 100644 --- a/infer/src/concurrency/MethodMatcher.ml +++ b/infer/src/concurrency/MethodMatcher.ml @@ -1,5 +1,5 @@ (* - * Copyright (c) 2017-present, Facebook, Inc. + * Copyright (c) 2018-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -54,8 +54,12 @@ let of_record {search_superclasses; method_prefix; actuals_pred; classname; meth |> Staged.unstage -let empty = - {search_superclasses= None; method_prefix= None; actuals_pred= None; classname= ""; methods= []} +let default = + { search_superclasses= Some true + ; method_prefix= Some false + ; actuals_pred= Some (fun _ -> true) + ; classname= "" + ; methods= [] } let of_list matchers tenv pn actuals = List.exists matchers ~f:(fun m -> m tenv pn actuals) @@ -82,7 +86,7 @@ let of_json top_json = | _ -> error json in - (match json with `Assoc fields -> parse_fields fields empty | _ -> error json) |> of_record + (match json with `Assoc fields -> parse_fields fields default | _ -> error json) |> of_record in match top_json with | `List matchers_json -> diff --git a/infer/src/concurrency/MethodMatcher.mli b/infer/src/concurrency/MethodMatcher.mli index befc2a575..5c39959ae 100644 --- a/infer/src/concurrency/MethodMatcher.mli +++ b/infer/src/concurrency/MethodMatcher.mli @@ -1,5 +1,5 @@ (* - * Copyright (c) 2017-present, Facebook, Inc. + * Copyright (c) 2018-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -34,8 +34,9 @@ type record = ; classname: string ; methods: string list } -val empty : record -(** record with empty strings, lists and None where appropriate. Useful for [with] expressions *) +val default : record +(** record encapsulating the default arguments of [call_matches]. [classname=""] and [methods=[]]. + Useful for [with] expressions *) val of_record : record -> t (** make a matcher out of a record; optional values use defaults *) diff --git a/infer/src/concurrency/StarvationModels.ml b/infer/src/concurrency/StarvationModels.ml index 68f092003..a3f87871b 100644 --- a/infer/src/concurrency/StarvationModels.ml +++ b/infer/src/concurrency/StarvationModels.ml @@ -94,22 +94,23 @@ let empty_or_excessive_timeout actuals = let strict_mode_matcher = let open MethodMatcher in let open StarvationDomain.Event in - (* NB [empty] searches superclasses too. Most of the classes below are final and we don't + (* NB [default] searches superclasses too. Most of the classes below are final and we don't really want to search superclasses for those that aren't, so for performance, disable that *) - let empty = {empty with search_superclasses= Some false} in + let dont_search_superclasses = {default with search_superclasses= Some false} in let matcher_records = - [ { empty with + [ { dont_search_superclasses with classname= "dalvik.system.BlockGuard$Policy"; methods= ["on"]; method_prefix= Some true } - ; {empty with classname= "java.lang.System"; methods= ["gc"; "runFinalization"]} - ; {empty with classname= "java.lang.Runtime"; methods= ["gc"]} - ; {empty with classname= "java.net.Socket"; methods= ["connect"]} + ; { dont_search_superclasses with + classname= "java.lang.System"; methods= ["gc"; "runFinalization"] } + ; {dont_search_superclasses with classname= "java.lang.Runtime"; methods= ["gc"]} + ; {dont_search_superclasses with classname= "java.net.Socket"; methods= ["connect"]} (* all public constructors of Socket with two or more arguments call connect *) - ; { empty with + ; { dont_search_superclasses with classname= "java.net.Socket" ; methods= [Typ.Procname.Java.constructor_method_name] ; actuals_pred= Some (function [] | [_] -> false | _ -> true) } - ; {empty with classname= "java.net.DatagramSocket"; methods= ["connect"]} - ; { empty with + ; {dont_search_superclasses with classname= "java.net.DatagramSocket"; methods= ["connect"]} + ; { dont_search_superclasses with classname= "java.io.File" ; methods= [ "canRead" @@ -136,9 +137,7 @@ let strict_mode_matcher = ; "setReadOnly" ; "setWritable" ] } ] in - let matcher = - of_list (StrictModeModels.is_strict_mode_violation :: List.map matcher_records ~f:of_record) - in + let matcher = of_list (List.map matcher_records ~f:of_record) in (matcher, High) @@ -146,37 +145,37 @@ let standard_matchers = let open MethodMatcher in let open StarvationDomain.Event in let high_sev = - [ {empty with classname= "java.lang.Thread"; methods= ["sleep"]} - ; { empty with + [ {default with classname= "java.lang.Thread"; methods= ["sleep"]} + ; { default with classname= "java.lang.Object" ; methods= ["wait"] ; actuals_pred= Some empty_or_excessive_timeout } - ; { empty with + ; { default with classname= "java.util.concurrent.CountDownLatch" ; methods= ["await"] ; actuals_pred= Some empty_or_excessive_timeout } (* an IBinder.transact call is an RPC. If the 4th argument (5th counting `this` as the first) is int-zero then a reply is expected and returned from the remote process, thus potentially blocking. If the 4th argument is anything else, we assume a one-way call which doesn't block. *) - ; { empty with + ; { default with classname= "android.os.IBinder" ; methods= ["transact"] ; actuals_pred= Some (fun actuals -> List.nth actuals 4 |> Option.value_map ~default:false ~f:HilExp.is_int_zero ) } - ; { empty with + ; { default with classname= "android.accounts.AccountManager" ; methods= ["setUserData"] ; search_superclasses= Some false } ] in let low_sev = - [ { empty with + [ { default with classname= "java.util.concurrent.Future" ; methods= ["get"] ; actuals_pred= Some empty_or_excessive_timeout ; search_superclasses= Some false } - ; { empty with + ; { default with classname= "android.os.AsyncTask" ; methods= ["get"] ; actuals_pred= Some empty_or_excessive_timeout } ] diff --git a/infer/src/concurrency/StrictModeModels.ml b/infer/src/concurrency/StrictModeModels.ml deleted file mode 100644 index 7a1b2868d..000000000 --- a/infer/src/concurrency/StrictModeModels.ml +++ /dev/null @@ -1,263 +0,0 @@ -(* - * Copyright (c) 2018-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -open! IStd -open MethodMatcher - -(* frameworks/base/core/java/android/app/backup/BackupAgent.java *) -let is_BackupAgent_method = - call_matches "android.app.backup.BackupAgent" - ["onRestoreFile" (* onRestoreFile(ParcelFileDescriptor,long,int,String,String,long,long) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/app/DownloadManager.java *) -let is_DownloadManager_method = - call_matches "android.app.DownloadManager" ["rename" (* rename(Context,long,String) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/app/NotificationManager.java *) -let is_NotificationManager_method = - call_matches "android.app.NotificationManager" - ["notifyAsUser" (* notifyAsUser(String,int,Notification,UserHandle) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/content/pm/ActivityInfo.java *) -let is_ActivityInfo_method = - call_matches "android.content.pm.ActivityInfo" ["dump" (* dump(Printer,String,int) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/content/pm/ApplicationInfo.java *) -let is_ApplicationInfo_method = - call_matches "android.content.pm.ApplicationInfo" - [ "dump" - ; (* dump(Printer,String,int) *) - "getHiddenApiEnforcementPolicy" - ; (* getHiddenApiEnforcementPolicy() *) - "maybeUpdateHiddenApiEnforcementPolicy" - (* maybeUpdateHiddenApiEnforcementPolicy(int,int) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/content/pm/ProviderInfo.java *) -let is_ProviderInfo_method = - call_matches "android.content.pm.ProviderInfo" ["dump" (* dump(Printer,String,int) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/content/pm/ResolveInfo.java *) -let is_ResolveInfo_method = - call_matches "android.content.pm.ResolveInfo" ["dump" (* dump(Printer,String,int) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/content/pm/ServiceInfo.java *) -let is_ServiceInfo_method = - call_matches "android.content.pm.ServiceInfo" ["dump" (* dump(Printer,String,int) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java *) -let is_SQLiteDatabase_method = - call_matches "android.database.sqlite.SQLiteDatabase" - [ "addCustomFunction" - ; (* addCustomFunction(String,int,SQLiteDatabase$CustomFunction) *) - "reopenReadWrite" - (* reopenReadWrite() *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/ddm/DdmHandleHeap.java *) -let is_DdmHandleHeap_method = - call_matches "android.ddm.DdmHandleHeap" ["handleChunk" (* handleChunk(Chunk) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/net/Uri.java *) -let is_Uri_method = - call_matches "android.net.Uri" ["getCanonicalUri" (* getCanonicalUri() *) - ] |> Staged.unstage - - -(* frameworks/base/core/java/android/os/Environment.java *) -let is_Environment_method = - call_matches "android.os.Environment" - ["classifyExternalStorageDirectory" (* classifyExternalStorageDirectory(File) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/os/Parcel.java *) -let is_Parcel_method = - call_matches "android.os.Parcel" ["readExceptionCode" (* readExceptionCode() *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/os/RecoverySystem.java *) -let is_RecoverySystem_method = - call_matches "android.os.RecoverySystem" - [ "handleAftermath" - ; (* handleAftermath(Context) *) - "rebootPromptAndWipeUserData" - ; (* rebootPromptAndWipeUserData(Context,String) *) - "rebootWipeCache" - ; (* rebootWipeCache(Context,String) *) - "rebootWipeUserData" - ; (* rebootWipeUserData(Context,boolean) *) - "rebootWipeUserData" - ; (* rebootWipeUserData(Context,boolean,String,boolean) *) - "rebootWipeUserData" - ; (* rebootWipeUserData(Context,boolean,String,boolean,boolean) *) - "rebootWipeUserData" - (* rebootWipeUserData(Context,String) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/os/storage/StorageManager.java *) -let is_StorageManager_method = - call_matches "android.os.storage.StorageManager" - [ "getPrimaryStoragePathAndSize" - ; (* getPrimaryStoragePathAndSize() *) - "getPrimaryStorageSize" - ; (* getPrimaryStorageSize() *) - "getStorageBytesUntilLow" - ; (* getStorageBytesUntilLow(File) *) - "getStorageCacheBytes" - ; (* getStorageCacheBytes(File,int) *) - "getStorageLowBytes" - (* getStorageLowBytes(File) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/os/StrictMode.java *) -let is_StrictMode_method = - call_matches "android.os.StrictMode" - [ "conditionallyCheckInstanceCounts" - ; (* conditionallyCheckInstanceCounts() *) - "decrementExpectedActivityCount" - ; (* decrementExpectedActivityCount(Class) *) - "noteDiskRead" - ; (* noteDiskRead() *) - "noteDiskWrite" - ; (* noteDiskWrite() *) - "noteResourceMismatch" - ; (* noteResourceMismatch(Object) *) - "noteUnbufferedIO" - ; (* noteUnbufferedIO() *) - "queueIdle" - (* queueIdle() *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/util/AtomicFile.java *) -let is_AtomicFile_method = - call_matches "android.util.AtomicFile" - ["getLastModifiedTime"; (* getLastModifiedTime() *) "startWrite" (* startWrite(long) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/webkit/WebViewFactory.java *) -let is_WebViewFactory_method = - call_matches "android.webkit.WebViewFactory" - ["onWebViewProviderChanged" (* onWebViewProviderChanged(PackageInfo) *) - ] - |> Staged.unstage - - -(* frameworks/base/core/java/android/webkit/WebViewLibraryLoader.java *) -let is_WebViewLibraryLoader_method = - call_matches "android.webkit.WebViewLibraryLoader" - ["getWebViewNativeLibrary" (* getWebViewNativeLibrary(PackageInfo,boolean) *) - ] - |> Staged.unstage - - -(* frameworks/base/media/java/android/media/MiniThumbFile.java *) -let is_MiniThumbFile_method = - call_matches "android.media.MiniThumbFile" - [ "eraseMiniThumb" - ; (* eraseMiniThumb(long) *) - "getMagic" - ; (* getMagic(long) *) - "getMiniThumbFromFile" - ; (* getMiniThumbFromFile(long,byte[]) *) - "saveMiniThumbToFile" - (* saveMiniThumbToFile(byte[],long,long) *) - ] - |> Staged.unstage - - -(* frameworks/base/media/java/android/media/RingtoneManager.java *) -let is_RingtoneManager_method = - call_matches "android.media.RingtoneManager" - ["deleteExternalRingtone" (* deleteExternalRingtone(Uri) *) - ] - |> Staged.unstage - - -(* frameworks/multidex/library/src/androidx/multidex/MultiDex.java *) -let is_MultiDex_method = - call_matches "androidx.multidex.MultiDex" - [ "install" - ; (* install(Context) *) - "installInstrumentation" - (* installInstrumentation(Context,Context) *) - ] - |> Staged.unstage - - -(* libcore/ojluni/src/main/java/java/util/logging/FileHandler.java *) -let is_FileHandler_method = - call_matches "java.util.logging.FileHandler" ["run" (* run() *) - ] |> Staged.unstage - - -let is_strict_mode_violation = - let matchers = - [ is_BackupAgent_method - ; is_DownloadManager_method - ; is_NotificationManager_method - ; is_ActivityInfo_method - ; is_ApplicationInfo_method - ; is_ProviderInfo_method - ; is_ResolveInfo_method - ; is_ServiceInfo_method - ; is_SQLiteDatabase_method - ; is_DdmHandleHeap_method - ; is_Uri_method - ; is_Environment_method - ; is_Parcel_method - ; is_RecoverySystem_method - ; is_StorageManager_method - ; is_StrictMode_method - ; is_AtomicFile_method - ; is_WebViewFactory_method - ; is_WebViewLibraryLoader_method - ; is_MiniThumbFile_method - ; is_RingtoneManager_method - ; is_MultiDex_method - ; is_FileHandler_method ] - in - fun tenv pn actuals -> List.exists matchers ~f:(fun m -> m tenv pn actuals) diff --git a/infer/src/concurrency/StrictModeModels.mli b/infer/src/concurrency/StrictModeModels.mli deleted file mode 100644 index acf073e88..000000000 --- a/infer/src/concurrency/StrictModeModels.mli +++ /dev/null @@ -1,11 +0,0 @@ -(* - * Copyright (c) 2018-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -open! IStd - -val is_strict_mode_violation : Tenv.t -> Typ.Procname.t -> HilExp.t list -> bool -(** is the method a potential strict mode violation, given the actuals passed? *)