diff --git a/infer/src/IR/HilExp.ml b/infer/src/IR/HilExp.ml index b5ddb9ce2..3bfb86f47 100644 --- a/infer/src/IR/HilExp.ml +++ b/infer/src/IR/HilExp.ml @@ -203,7 +203,7 @@ let of_sil ~include_array_indexes ~f_resolve_id ~add_deref exp typ = let is_null_literal = function Constant (Cint n) -> IntLit.isnull n | _ -> false -let is_int_zero = function Constant Const.Cint i -> IntLit.iszero i | _ -> false +let is_int_zero = function Constant (Const.Cint i) -> IntLit.iszero i | _ -> false let rec eval_arithmetic_binop op e1 e2 = match (eval e1, eval e2) with diff --git a/infer/src/concurrency/starvation.ml b/infer/src/concurrency/starvation.ml index 446b338d9..dfb358898 100644 --- a/infer/src/concurrency/starvation.ml +++ b/infer/src/concurrency/starvation.ml @@ -20,6 +20,16 @@ let is_java_static pname = false +let is_countdownlatch_await pn = + match pn with + | Typ.Procname.Java java_pname -> + let classname = Typ.Procname.Java.get_class_name java_pname in + let mthd = Typ.Procname.Java.get_method java_pname in + String.equal classname "java.util.concurrent.CountDownLatch" && String.equal mthd "await" + | _ -> + false + + (* 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. @@ -95,8 +105,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | LockedIfTrue -> astate | NoEffect -> - if is_two_way_binder_transact tenv actuals callee_pname then - Domain.blocking_call callee_pname loc astate + if + is_countdownlatch_await callee_pname + || is_two_way_binder_transact tenv actuals callee_pname + then Domain.blocking_call callee_pname loc astate else if is_on_main_thread callee_pname then Domain.set_on_main_thread astate else Summary.read_summary pdesc callee_pname diff --git a/infer/tests/codetoanalyze/java/starvation/Countdwn.java b/infer/tests/codetoanalyze/java/starvation/Countdwn.java new file mode 100644 index 000000000..032d666c8 --- /dev/null +++ b/infer/tests/codetoanalyze/java/starvation/Countdwn.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 - 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. + */ + +import java.util.concurrent.CountDownLatch; +import android.support.annotation.UiThread; + +class Countdwn { + CountDownLatch latch; + + void awaitOnMainByCallBad() throws InterruptedException { + OurThreadUtils.assertMainThread(); + latch.await(); + } + + @UiThread + void awaitOnMainByAnnotBad() throws InterruptedException { + latch.await(); + } + + void countDownOk() throws InterruptedException { + OurThreadUtils.assertMainThread(); + latch.countDown(); + } + + void awaitOnAnyThreadOk() throws InterruptedException { + latch.await(); + } +} diff --git a/infer/tests/codetoanalyze/java/starvation/issues.exp b/infer/tests/codetoanalyze/java/starvation/issues.exp index 26ac59424..33bcaa410 100644 --- a/infer/tests/codetoanalyze/java/starvation/issues.exp +++ b/infer/tests/codetoanalyze/java/starvation/issues.exp @@ -1,6 +1,8 @@ codetoanalyze/java/starvation/Binders.java, void Binders.annotationBad(), 0, STARVATION, ERROR, [Method start: void Binders.annotationBad(),Method call: void Binders.doTransact(),Calls boolean Binder.transact(int,Parcel,Parcel,int)] codetoanalyze/java/starvation/Binders.java, void Binders.interBad(), 0, STARVATION, ERROR, [Method start: void Binders.interBad(),Calls boolean Binder.transact(int,Parcel,Parcel,int)] codetoanalyze/java/starvation/Binders.java, void Binders.intraBad(), 0, STARVATION, ERROR, [Method start: void Binders.intraBad(),Method call: void Binders.doTransact(),Calls boolean Binder.transact(int,Parcel,Parcel,int)] +codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByAnnotBad(), 0, STARVATION, ERROR, [Method start: void Countdwn.awaitOnMainByAnnotBad(),Calls void CountDownLatch.await()] +codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByCallBad(), 0, STARVATION, ERROR, [Method start: void Countdwn.awaitOnMainByCallBad(),Calls void CountDownLatch.await()] codetoanalyze/java/starvation/InnerClass.java, void InnerClass$InnerClassA.innerOuterBad(), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InnerClass$InnerClassA*,Method call: void InnerClass.bar(),Locks this in class InnerClass*,[Trace 2] Locks this in class InnerClass*,Method call: void InnerClass$InnerClassA.baz(),Locks this in class InnerClass$InnerClassA*] codetoanalyze/java/starvation/InnerClass.java, void InnerClass.outerInnerBad(InnerClass$InnerClassA), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InnerClass*,Method call: void InnerClass$InnerClassA.baz(),Locks this in class InnerClass$InnerClassA*,[Trace 2] Method start: InnerClass$InnerClassA.(InnerClass,Object),Locks this in class InnerClass$InnerClassA*,Method call: void InnerClass.bar(),Locks this in class InnerClass*] codetoanalyze/java/starvation/Interclass.java, void InterclassA.interclass2Bad(Interclass), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InterclassA*,Method call: void Interclass.interclass2Bad(),Locks this in class Interclass*,[Trace 2] Locks this in class Interclass*,Method call: void InterclassA.interclass1Bad(),Locks this in class InterclassA*]