diff --git a/infer/src/concurrency/RacerDModels.ml b/infer/src/concurrency/RacerDModels.ml index a9e009557..6566e8387 100644 --- a/infer/src/concurrency/RacerDModels.ml +++ b/infer/src/concurrency/RacerDModels.ml @@ -433,11 +433,23 @@ let should_flag_interface_call tenv exps call_flags pname = false +(** Set of standard classes/interfaces that guarantee thread-safe access. This list is heavily + deduplicated using the inheritance relation as represented in Infer, that is, any class that + implements/inherits the interfaces/classes below is considered to be thread-safe. For instance, + all thread-safe maps implement [ConcurrentMap] and thus need not be explicitly represented. On + the other hand, there is no equivalent interface for sets [ConcurrentSet], so all set-like + classes have to be listed. Information is mostly drawn from + https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html *) let synchronized_container_classes = [ "android.support.v4.util.Pools$SynchronizedPool" ; "androidx.core.util.Pools$SynchronizedPool" + ; "java.util.concurrent.BlockingDeque" + ; "java.util.concurrent.BlockingQueue" ; "java.util.concurrent.ConcurrentMap" - ; "java.util.concurrent.CopyOnWriteArrayList" ] + ; "java.util.concurrent.ConcurrentSkipListSet" + ; "java.util.concurrent.CopyOnWriteArrayList" + ; "java.util.concurrent.CopyOnWriteArraySet" + ; "java.util.Hashtable" ] let is_synchronized_container_constructor = diff --git a/infer/tests/codetoanalyze/java/racerd/Containers.java b/infer/tests/codetoanalyze/java/racerd/Containers.java index ecd787213..13dc9f5a6 100644 --- a/infer/tests/codetoanalyze/java/racerd/Containers.java +++ b/infer/tests/codetoanalyze/java/racerd/Containers.java @@ -16,10 +16,13 @@ import android.util.SparseArray; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.concurrent.ThreadSafe; @@ -330,4 +333,26 @@ class Containers { synchronized void raceWithSizeBad(String value) { mSomeOtherList.remove(value); } + + Map mSomeMap = new HashMap(); + + int getMapSizeBad() { + return mSomeMap.size(); + } + + synchronized void raceWithMapSizeBad(String value) { + mSomeMap.remove(value); + } + + Map mSomeOtherMap = new Hashtable(); + + void writeToHashtableOk(String value) { + mSomeOtherMap.remove(value); + } + + Set mConcurrentSet = new ConcurrentSkipListSet(); + + void dynamicallyTypedConcurrentSetAddOk(String value) { + mConcurrentSet.add(value); + } } diff --git a/infer/tests/codetoanalyze/java/racerd/issues.exp b/infer/tests/codetoanalyze/java/racerd/issues.exp index 76a23a3e9..1d7bcaba9 100644 --- a/infer/tests/codetoanalyze/java/racerd/issues.exp +++ b/infer/tests/codetoanalyze/java/racerd/issues.exp @@ -26,29 +26,30 @@ codetoanalyze/java/racerd/Constructors.java, Constructors.FP_singleton2Ok():Cons codetoanalyze/java/racerd/Constructors.java, Constructors.singleton1Bad():Constructors, 57, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [call to Constructors.(Object),access to `Constructors.staticField`] codetoanalyze/java/racerd/Constructors.java, Constructors.singleton2Bad():Constructors, 63, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,access to `Constructors.sSingleton2`,,access to `Constructors.sSingleton2`] codetoanalyze/java/racerd/Constructors.java, Constructors.singleton2Bad():Constructors, 64, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `Constructors.sSingleton2`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSimpleArrayMapBad(android.support.v4.util.SimpleArrayMap):void, 279, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `map` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayBad(android.util.SparseArray):void, 269, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `sparseArray` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayCompatBad(android.support.v4.util.SparseArrayCompat):void, 260, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `sparseArray` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToUnsynchronizedListBad(java.lang.String):void, 321, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mSomeList` via call to `add`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.containerWrapperUnownedWriteBad(java.lang.Object):void, 167, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [call to Object ContainerWrapper.write(Object),call to Object ContainerWrapper._write(Object),Write to container `this.mContainerWrapper.children` via call to `add`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.getListSizeBad():int, 327, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.mSomeOtherList` via call to `size`,,Write to container `this.mSomeOtherList` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddAllBad(java.util.Collection):void, 55, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `addAll`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad1(java.lang.String):void, 47, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `add`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad2(int,java.lang.String):void, 51, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `add`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listClearBad():void, 59, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `clear`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listReadBad(java.lang.String):boolean, 98, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.mListSyncWrites` via call to `contains`,,Write to container `this.mListSyncWrites` via call to `add`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveAllBad(java.util.Collection):void, 71, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `removeAll`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad1(int):void, 63, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad2(java.lang.String):void, 67, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSetBad(int,java.lang.String):void, 75, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `set`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSubclassWriteBad(java.util.ArrayList,int):void, 79, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `list` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapClearBad():void, 115, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `clear`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutAllBad(java.util.Map):void, 119, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `putAll`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutBad(java.lang.String,java.lang.String):void, 107, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapRemoveBad(java.lang.String):void, 111, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapSubclassWriteBad(java.util.HashMap,java.lang.String):void, 137, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `m` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.poolBad():void, 298, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.simplePool` via call to `release`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.readSimpleArrayMap():int, 284, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.si_map` via call to `get`,,Write to container `this.si_map` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSimpleArrayMapBad(android.support.v4.util.SimpleArrayMap):void, 282, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `map` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayBad(android.util.SparseArray):void, 272, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `sparseArray` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayCompatBad(android.support.v4.util.SparseArrayCompat):void, 263, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `sparseArray` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToUnsynchronizedListBad(java.lang.String):void, 324, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mSomeList` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.containerWrapperUnownedWriteBad(java.lang.Object):void, 170, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [call to Object ContainerWrapper.write(Object),call to Object ContainerWrapper._write(Object),Write to container `this.mContainerWrapper.children` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.getListSizeBad():int, 330, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.mSomeOtherList` via call to `size`,,Write to container `this.mSomeOtherList` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.getMapSizeBad():int, 340, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.mSomeMap` via call to `size`,,Write to container `this.mSomeMap` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddAllBad(java.util.Collection):void, 58, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `addAll`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad1(java.lang.String):void, 50, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad2(int,java.lang.String):void, 54, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listClearBad():void, 62, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `clear`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listReadBad(java.lang.String):boolean, 101, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.mListSyncWrites` via call to `contains`,,Write to container `this.mListSyncWrites` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveAllBad(java.util.Collection):void, 74, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `removeAll`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad1(int):void, 66, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad2(java.lang.String):void, 70, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSetBad(int,java.lang.String):void, 78, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mList` via call to `set`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSubclassWriteBad(java.util.ArrayList,int):void, 82, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `list` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapClearBad():void, 118, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `clear`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutAllBad(java.util.Map):void, 122, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `putAll`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutBad(java.lang.String,java.lang.String):void, 110, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapRemoveBad(java.lang.String):void, 114, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.mMap` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapSubclassWriteBad(java.util.HashMap,java.lang.String):void, 140, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `m` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.poolBad():void, 301, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [Write to container `this.simplePool` via call to `release`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.readSimpleArrayMap():int, 287, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [,Read of container `this.si_map` via call to `get`,,Write to container `this.si_map` via call to `put`] codetoanalyze/java/racerd/DeepOwnership.java, DeepOwnership.globalNotOwnedBad():void, 16, THREAD_SAFETY_VIOLATION, no_bucket, WARNING, [access to `DeepOwnership.global.next`] codetoanalyze/java/racerd/Dispatch.java, codetoanalyze.java.checkers.Dispatch.callUnannotatedInterfaceBad(codetoanalyze.java.checkers.UnannotatedInterface):void, 49, INTERFACE_NOT_THREAD_SAFE, no_bucket, WARNING, [Call to un-annotated interface method void UnannotatedInterface.foo()] codetoanalyze/java/racerd/Dispatch.java, codetoanalyze.java.checkers.Dispatch.callUnannotatedInterfaceIndirectBad(codetoanalyze.java.checkers.NotThreadSafe,codetoanalyze.java.checkers.UnannotatedInterface):void, 53, INTERFACE_NOT_THREAD_SAFE, no_bucket, WARNING, [call to void NotThreadSafe.notThreadSafeOk(UnannotatedInterface),Call to un-annotated interface method void UnannotatedInterface.foo()]