From 72e778d094c1f3a68d3f7655c0e4d901333ad1be Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Mon, 17 Jul 2017 13:38:37 -0700 Subject: [PATCH] [thread-safety] tests for escape analysis Summary: We get the wrong answer on most of them for now, but that is expected Reviewed By: ngorogiannis Differential Revision: D5429242 fbshipit-source-id: 4899079 --- .../java/threadsafety/Escape.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 infer/tests/codetoanalyze/java/threadsafety/Escape.java diff --git a/infer/tests/codetoanalyze/java/threadsafety/Escape.java b/infer/tests/codetoanalyze/java/threadsafety/Escape.java new file mode 100644 index 000000000..f746d4b54 --- /dev/null +++ b/infer/tests/codetoanalyze/java/threadsafety/Escape.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 201y - 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. + */ + +package codetoanalyze.java.checkers; + +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +public class Escape { + + private Obj mField; + private static Obj sGlobal; + + // this can race with unsafe writes to mField.f + public synchronized Object racyRead1() { + return mField.f; + } + + // this can race with unsafe writes to mField.f + public synchronized Object racyRead2() { + return sGlobal.f; + } + + public void FN_fieldEscapeBad() { + Obj o = new Obj(); + synchronized(this) { + mField = o; + } + o.f = new Object(); // not safe + } + + public void FN_globalEscapeBad() { + Obj o = new Obj(); + synchronized (Escape.class) { + sGlobal = o; + } + o.f = new Object(); // not safe + } + + public synchronized void escapeInCallee(Obj o) { + mField = o; + } + + public void FN_escapeInCalleeBad() { + Obj o = new Obj(); + escapeInCallee(o); + o.f = new Object(); + } + + public void aliasOk() { + Obj o = new Obj(); // though there's two pointers to this address, neither escapes + Obj alias = o; + o.f = null; + alias.f = null; + } + + public void nonAliasReadOk() { + Obj o = new Obj(); + String s = o + "a"; + o.f = null; // ok + } + + public void FN_escapeViaAliasBad1() { + Obj o = new Obj(); + Obj alias = o; + escapeInCallee(alias); + o.f = null; // bad + alias.f = null; // bad + } + + public void FN_escapeViaAliasBad2() { + Obj o = new Obj(); + Obj alias = o; + escapeInCallee(o); + o.f = null; // bad + alias.f = null; // bad + } + + public Obj id(Obj o) { + return o; + } + + public void FN_aliasViaReturnBad1() { + Obj o = new Obj(); + Obj alias = id(o); + escapeInCallee(alias); + o.f = null; // bad + alias.f = null; // bad + } + + public void FN_aliasViaReturnBad2() { + Obj o = new Obj(); + Obj alias = id(o); + escapeInCallee(o); + o.f = null; // bad + alias.f = null; // bad + } + + private void twoParamsOneEscapes(Obj o1, Obj o2) { + synchronized (Escape.class) { + sGlobal = o1; + } + o1.f = null; // only safe if o1/o2 not aliased + } + + public void FN_aliasedParamsBad() { + Obj o = new Obj(); + twoParamsOneEscapes(o, o); // should report racy write in callee + o.f = null; // bad + } + + public void nonAliasedParamsOk() { + Obj o1 = new Obj(); + Obj o2 = new Obj(); + twoParamsOneEscapes(o1, o2); + o2.f = null; // ok + } + +} + +@ThreadSafe +class Leaky { + + Leaky mLeak; + Object mField; + Object sGlobal; + + public Leaky() { + mLeak = this; + } + + public void FN_leakyConstructorBad() { + Leaky l = new Leaky(); + synchronized (Leaky.class) { + sGlobal = l.mLeak; // oops, this leaks l + } + l.mField = 1; // bad + } + +}