You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

577 lines
12 KiB

/*
* 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.
*/
package codetoanalyze.java.infer;
import android.annotation.SuppressLint;
import com.google.common.annotations.VisibleForTesting;
import java.io.Closeable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.concurrent.GuardedBy;
public class GuardedByExample {
static class AutoCloseableReadWriteUpdateLock implements Closeable {
@Override
public void close() {}
}
private Object mLock = new Object();
private Object mOtherLock = new Object();
private AutoCloseableReadWriteUpdateLock mReadWriteLock = new AutoCloseableReadWriteUpdateLock();
@GuardedBy("mLock")
private Object f = new Object();
@GuardedBy("this")
Object g = new Object();
Object mCopyOfG;
@GuardedBy("SomeLockThatDoesntExist")
Object h = new Object();
@GuardedBy("mReadWriteLock")
Object i = new Object();
private static Object sLock = new Object();
@GuardedBy("sLock")
static Object sFld;
@GuardedBy("GuardedByExample.class")
static Object sGuardedByClass;
static {
// don't warn on class initializer
sFld = new Object();
}
public GuardedByExample() {
// don't warn on reads or writes of Guarded fields in constructor
f.toString();
g = new Object();
}
void readFBad() {
this.f.toString();
}
@SuppressLint("InvalidAccessToGuardedField")
void readFBadButSuppressed() {
this.f.toString();
}
@SuppressLint("SomeOtherWarning")
void readFBadButSuppressedOther() {
this.f.toString();
}
void writeFBad() {
this.f = new Object();
}
void readFBadWrongLock() {
synchronized (mOtherLock) {
this.f.toString(); // f is supposed to be protected by mLock
}
}
void writeFBadWrongLock() {
synchronized (mOtherLock) {
this.f = new Object(); // f is supposed to be protected by mLock
}
}
void readFAfterBlockBad() {
synchronized (mLock) {
}
this.f.toString();
}
void writeFAfterBlockBad() {
synchronized (mLock) {
}
this.f = new Object();
}
@GuardedBy("mOtherLock")
void readFBadWrongAnnotation() {
this.f.toString();
}
@GuardedBy("mLock")
void readFOkMethodAnnotated() {
this.f.toString();
}
synchronized void synchronizedMethodReadOk() {
this.g.toString();
}
synchronized void synchronizedMethodWriteOk() {
this.g = new Object();
}
void readFOkSynchronized() {
synchronized (mLock) {
this.f.toString();
}
}
void writeFOkSynchronized() {
synchronized (mLock) {
this.f = new Object();
}
}
synchronized void synchronizedMethodReadBad() {
this.f.toString(); // f is supposed to be protected by mLock, not this
}
synchronized void synchronizedMethodWriteBad() {
this.f = new Object(); // f is supposed to be protected by mLock, not this
}
void reassignCopyOk() {
synchronized (this) {
mCopyOfG = g; // these are ok: access of g guarded by this
}
mCopyOfG = new Object(); // ok; this doesn't change the value of g
}
void readHBad() {
synchronized (mLock) { // h is not protected by mLock
this.h.toString();
}
}
synchronized void readHBadSynchronizedMethodShouldntHelp() {
this.h.toString(); // h is not protected by this
}
private void privateUnguardedAccess() {
// not protected, but safe if all call sites guard the access to f
this.g.toString();
}
public void guardedCallSite1() {
synchronized (this) {
privateUnguardedAccess(); // should not warn; lock is held
}
}
public synchronized void guardedCallSite2() {
privateUnguardedAccess(); // should not warn; lock is held
}
private void wrapper() {
privateUnguardedAccess(); // should not warn, just propagate the proof obl
}
public void guardedCallSite3() {
synchronized (this) {
wrapper(); // should not warn
}
}
void readWriteLockOk() {
try (AutoCloseableReadWriteUpdateLock lock = mReadWriteLock) {
this.i.toString();
}
}
static synchronized void staticSynchronizedOk() {
sGuardedByClass.toString();
}
static void synchronizeOnClassOk1() {
synchronized (GuardedByExample.class) {
sGuardedByClass.toString(); // should not warn here
sGuardedByClass = new Object(); // or here
}
}
void synchronizedOnThisBad() {
sGuardedByClass.toString();
}
Object dontReportOnCompilerGenerated() {
return new Object() {
public void accessInAnonClassOk() {
synchronized (mLock) {
f.toString();
}
}
};
}
Object readFromInnerClassOkOuter() {
return new Object() {
public String readFromInnerClassOk() {
synchronized (GuardedByExample.this) {
return g.toString();
}
}
};
}
Object readFromInnerClassBad1Outer() {
return new Object() {
public String readFromInnerClassBad1() {
synchronized (this) {
return g.toString(); // g is guarded by the outer class this, not this$0
}
}
};
}
Object readFromInnerClassBad2Outer() {
return new Object() {
public synchronized String readFromInnerClassBad2() {
return g.toString(); // g is guarded by the outer class this, not this$0
}
};
}
@VisibleForTesting
public void visibleForTestingOk1() {
f.toString(); // should push proof obl to caller
}
@VisibleForTesting
void visibleForTestingOk2() {
f.toString(); // should push proof obl to caller
}
synchronized Object returnPtG() {
return g;
}
// note: this test should raise an error under "by value" GuardedBy semantics, but not under
// "by reference" GuardedBy semantics
void readGFromCopyOk() {
synchronized (this) {
mCopyOfG = g; // these are ok: access of g guarded by this
g.toString();
}
mCopyOfG.toString(); // should be an error; unprotected access to pt(g)
}
// another "by reference" vs "by value" test. buggy in "by value", but safe in "by reference"
void usePtG() {
Object ptG = returnPtG();
ptG.toString();
}
Object byRefTrickyBad() {
Object local = null;
synchronized (this) {
local = g; // we have a local pointer... to pt(G)
}
g.toString(); // ...but unsafe access is through g!
return local;
}
void byRefTrickyOk() {
Object local = null;
synchronized (this) {
local = g; // we have a local pointer... to pt(G)
}
local.toString(); // ...but unsafe access is through g!
}
@GuardedBy("ui_thread")
Object uiThread1;
@GuardedBy("ui-thread")
Object uiThread2;
@GuardedBy("uithread")
Object uiThread3;
@GuardedBy("something that's clearly not an expression")
Object nonExpression;
// tests for not reporting false alarms on unrecognized GuardedBy strings
void accessUnrecognizedGuardedByFieldsOk() {
uiThread1 = new Object();
uiThread1.toString();
uiThread2 = new Object();
uiThread2.toString();
uiThread3 = new Object();
uiThread3.toString();
nonExpression = new Object();
nonExpression.toString();
}
// outer class this tests
@GuardedBy("GuardedByExample.this")
Object guardedByOuterThis;
synchronized void okOuterAccess() {
guardedByOuterThis = null;
}
// inner class this tests
private class Inner {
@GuardedBy("this")
Object guardedByInnerThis1;
@GuardedBy("Inner.this")
Object guardedByInnerThis2;
@GuardedBy("GuardedByExample$Inner.this")
Object guardedByInnerThis3;
@GuardedBy("Inner.class")
Object guardedByInnerClass1;
@GuardedBy("GuardedByExample.Inner.class")
Object guardedByInnerClass2;
@GuardedBy("GuardedByExample$Inner.class")
Object guardedByInnerClass3;
synchronized void okAccess1() {
guardedByInnerThis1 = null;
}
synchronized void okAccess2() {
guardedByInnerThis2 = null;
}
synchronized void okAccess3() {
guardedByInnerThis3 = null;
}
void okInnerClassGuard1() {
synchronized (Inner.class) {
guardedByInnerClass1 = new Object();
guardedByInnerClass2 = new Object();
guardedByInnerClass3 = new Object();
}
}
void okInnerClassGuard2() {
synchronized (GuardedByExample.Inner.class) {
guardedByInnerClass1 = new Object();
guardedByInnerClass2 = new Object();
guardedByInnerClass3 = new Object();
}
}
}
// TODO: report on these cases
/*
public void unguardedCallSiteBad1() {
privateUnguardedAccess(); // should warn; lock is not held
}
protected void unguardedCallSiteBad2() {
privateUnguardedAccess(); // should warn; lock is not held
}
void unguardedCallSiteBad3() {
privateUnguardedAccess(); // should warn; lock is not held
}
*/
int n;
public void withloop2() {
synchronized (mLock) {
for (int i = 0; i <= n; i++) {
f = 42;
}
}
}
public void withoutloop2() {
synchronized (mLock) {
f = 42;
}
}
@GuardedBy("self_reference")
Object self_reference;
void guardedBySelfReferenceOK() {
synchronized (self_reference) {
this.self_reference.toString();
}
}
// TODO: report on this case, or at least a version which writes
/*
void guardedBySelfReferenceBad() {
this.self_reference.toString();
}
*/
@GuardedBy("itself")
Object itself_fld;
void itselfOK() {
synchronized (itself_fld) {
this.itself_fld.toString();
}
}
// TODO: report on this case, or at least a version which writes
/*
void itselfBad() {
this.itself_fld.toString();
}
*/
ReadWriteLock mRWL;
@GuardedBy("mRWL")
Integer guardedbymRWL;
Integer someOtherInt;
void readLockOK() {
mRWL.readLock().lock();
someOtherInt = guardedbymRWL;
mRWL.readLock().unlock();
}
void writeLockOK() {
mRWL.writeLock().lock();
guardedbymRWL = 55;
mRWL.writeLock().unlock();
}
ReentrantReadWriteLock mRRWL;
@GuardedBy("mRRWL")
Integer guardedbymRRWL;
void reentrantReadLockOK() {
mRRWL.readLock().lock();
someOtherInt = guardedbymRRWL;
mRRWL.readLock().unlock();
}
void reentrantWriteLockOK() {
mRRWL.writeLock().lock();
guardedbymRRWL = 55;
mRRWL.writeLock().unlock();
}
// TODO: warn on misuse of read/write locks.
@GuardedBy("this")
Integer xForSub;
static class Sub extends GuardedByExample {
void goodSub1() {
synchronized (this) {
xForSub = 22;
}
}
synchronized void goodSub2() {
xForSub = 22;
}
void badSub() {
xForSub = 22;
}
}
Lock normallock;
@GuardedBy("normallock")
Integer guardedbynl;
ReentrantLock reentrantlock;
@GuardedBy("reentrantlock")
Integer guardedbyrel;
void goodGuardedByNormalLock() {
normallock.lock();
guardedbynl = 22;
normallock.unlock();
}
void goodTryLockGuardedByNormalLock() {
if (normallock.tryLock()) {
guardedbynl = 22;
normallock.unlock();
}
}
void goodTryLockGuardedByReentrantLock() {
if (reentrantlock.tryLock()) {
guardedbyrel = 44;
reentrantlock.unlock();
}
}
void badGuardedByNormalLock() {
guardedbynl = 22;
}
void badGuardedByReentrantLock() {
guardedbyrel = 44;
}
static class OtherClassWithLock {
ReentrantLock lock;
@GuardedBy("lock")
Object guardedByLock;
Object otherClassObject;
void guardedInSameClassOk() {
lock.lock();
guardedByLock = new Object();
lock.unlock();
}
}
@GuardedBy("OtherClassWithLock.lock")
Object guardedByLock1;
@GuardedBy("codetoanalyze.java.infer.GuardedByExample$OtherClassWithLock.lock")
Object guardedByLock2;
@GuardedBy("OtherClassWithLock.otherClassObject")
Object guardedByLock3;
OtherClassWithLock otherClass;
void guardedByTypeSyntaxOk1() {
otherClass.lock.lock();
guardedByLock1 = true;
guardedByLock2 = true;
otherClass.lock.unlock();
}
void guardedByTypeSyntaxOk2() {
synchronized (otherClass.otherClassObject) {
guardedByLock3 = true;
}
}
void guardedByTypeSyntaxBad() {
guardedByLock1 = true;
guardedByLock2 = true;
}
}