From fb77efea6ab3a8e8f72968c56f6140eef85252cd Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Thu, 3 Oct 2019 05:08:34 -0700 Subject: [PATCH] [starvation] add test for master-lock false positive Summary: Holding a master lock and then acquiring two other locks inside can generate a false positive as shown. Reviewed By: mityal Differential Revision: D17710076 fbshipit-source-id: 5bc910ba2 --- .../java/starvation/MasterLock.java | 49 +++++++++++++++++++ .../codetoanalyze/java/starvation/issues.exp | 4 ++ 2 files changed, 53 insertions(+) create mode 100644 infer/tests/codetoanalyze/java/starvation/MasterLock.java diff --git a/infer/tests/codetoanalyze/java/starvation/MasterLock.java b/infer/tests/codetoanalyze/java/starvation/MasterLock.java new file mode 100644 index 000000000..354159036 --- /dev/null +++ b/infer/tests/codetoanalyze/java/starvation/MasterLock.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +class MasterLock { + Object a, b; + + // classic deadlock between 2 methods + // 1st method has a and wants b + void oneWayBad() { + synchronized (a) { + synchronized (b) { + } + } + } + + // 2nd method has b and wants a + void theOtherWayBad() { + synchronized (b) { + synchronized (a) { + } + } + } + + Object master, x, y; + + // both methods hold the master lock so cannot interleave + // and thus cannot deadlock + void FP_oneWayOk() { + synchronized (master) { + synchronized (x) { + synchronized (y) { + } + } + } + } + + void FP_theOtherWayOk() { + synchronized (master) { + synchronized (y) { + synchronized (x) { + } + } + } + } +} diff --git a/infer/tests/codetoanalyze/java/starvation/issues.exp b/infer/tests/codetoanalyze/java/starvation/issues.exp index 7a5c268fe..30f305058 100644 --- a/infer/tests/codetoanalyze/java/starvation/issues.exp +++ b/infer/tests/codetoanalyze/java/starvation/issues.exp @@ -25,6 +25,10 @@ codetoanalyze/java/starvation/LocklessTests.java, LocklessTestsB.locklessMethod( codetoanalyze/java/starvation/LocklessTests.java, LocklessTestsC.locklessMethod():void, 52, LOCKLESS_VIOLATION, no_bucket, ERROR, [`void LocklessTestsC.locklessMethod()`,Method call: `void LocklessTestsC.takeLock()`, locks `this` in `class LocklessTestsC`] codetoanalyze/java/starvation/MainThreadTest.java, AnnotatedClass.callTransactBad(MainThreadTest):void, 30, STARVATION, no_bucket, ERROR, [`void AnnotatedClass.callTransactBad(MainThreadTest)`,Method call: `void MainThreadTest.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`,[Trace on UI thread] `void AnnotatedClass.callTransactBad(MainThreadTest)`,class `AnnotatedClass` is annotated `UiThread`] codetoanalyze/java/starvation/MainThreadTest.java, MainThreadTest.callTransactBad():void, 23, STARVATION, no_bucket, ERROR, [`void MainThreadTest.callTransactBad()`,Method call: `void MainThreadTest.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`,[Trace on UI thread] `void MainThreadTest.callTransactBad()`,`void MainThreadTest.callTransactBad()` is annotated `UiThread`] +codetoanalyze/java/starvation/MasterLock.java, MasterLock.FP_oneWayOk():void, 34, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MasterLock.FP_oneWayOk()`, locks `this.x` in `class MasterLock`, locks `this.y` in `class MasterLock`,[Trace 2] `void MasterLock.FP_theOtherWayOk()`, locks `this.y` in `class MasterLock`, locks `this.x` in `class MasterLock`] +codetoanalyze/java/starvation/MasterLock.java, MasterLock.FP_theOtherWayOk():void, 43, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MasterLock.FP_theOtherWayOk()`, locks `this.y` in `class MasterLock`, locks `this.x` in `class MasterLock`,[Trace 2] `void MasterLock.FP_oneWayOk()`, locks `this.x` in `class MasterLock`, locks `this.y` in `class MasterLock`] +codetoanalyze/java/starvation/MasterLock.java, MasterLock.oneWayBad():void, 14, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MasterLock.oneWayBad()`, locks `this.a` in `class MasterLock`, locks `this.b` in `class MasterLock`,[Trace 2] `void MasterLock.theOtherWayBad()`, locks `this.b` in `class MasterLock`, locks `this.a` in `class MasterLock`] +codetoanalyze/java/starvation/MasterLock.java, MasterLock.theOtherWayBad():void, 22, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MasterLock.theOtherWayBad()`, locks `this.b` in `class MasterLock`, locks `this.a` in `class MasterLock`,[Trace 2] `void MasterLock.oneWayBad()`, locks `this.a` in `class MasterLock`, locks `this.b` in `class MasterLock`] codetoanalyze/java/starvation/MyActivity.java, MyActivity.onCreate(android.os.Bundle):void, 28, STARVATION, no_bucket, ERROR, [`void MyActivity.onCreate(Bundle)`,Method call: `void MyActivity.bad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`,[Trace on UI thread] `void MyActivity.onCreate(Bundle)`,`void MyActivity.onCreate(Bundle)` is a standard UI-thread method] codetoanalyze/java/starvation/MyActivity.java, MyActivity.onDestroy():void, 58, STARVATION, no_bucket, ERROR, [`void MyActivity.onDestroy()`,Method call: `void MyActivity.bad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`,[Trace on UI thread] `void MyActivity.onDestroy()`,`void MyActivity.onDestroy()` is a standard UI-thread method] codetoanalyze/java/starvation/MyActivity.java, MyActivity.onPause():void, 48, STARVATION, no_bucket, ERROR, [`void MyActivity.onPause()`,Method call: `void MyActivity.bad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`,[Trace on UI thread] `void MyActivity.onPause()`,`void MyActivity.onPause()` is a standard UI-thread method]