java: add ThreadSafe query (P3)

Co-authored-by: Raúl Pardo <raul.pardo@protonmail.com>
Co-authored-by: SimonJorgensenMancofi <simon.jorgensen@mancofi.dk>
Co-authored-by: Bjørnar Haugstad Jåtten <bjornjaat@hotmail.com>
This commit is contained in:
yoff
2025-01-13 21:46:00 +01:00
parent 83519a9fcc
commit fe487e8bf0
21 changed files with 1122 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package examples;
public class App {
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) {
System.out.println(new App().getGreeting());
}
}

View File

@@ -0,0 +1,72 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class C {
private int y;
private Lock lock = new ReentrantLock();
private Lock lock2 = new ReentrantLock();
public void m() {
this.y = 0; // $ Alert
this.y += 1;
this.y = this.y - 1;
}
public void n4() {
this.y = 0;
this.y += 1;
this.y = this.y - 1;
}
public void setY(int y) {
this.y = y;
}
public void test() {
if (y == 0) {
lock.lock();
}
y = 0;
lock.unlock();
}
public void n() {
this.lock.lock();
this.y = 0;
this.y += 1;
this.y = this.y - 1;
this.lock.unlock();
}
public void callTestLock2() {
lock2.lock();
setY(1);
lock2.unlock();
}
public void n2() {
lock.lock();
this.y = 0;
this.y += 1;
this.y = this.y - 1;
lock.unlock();
}
public void n3() {
lock.lock();
y = 0;
y += 1;
y = y - 1;
lock.unlock();
}
public void callTest() {
lock.lock();
setY(1);
lock.unlock();
}
}

View File

@@ -0,0 +1,40 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
class FaultyTurnstileExample {
private Lock lock = new ReentrantLock();
private int count = 0;
public void inc() {
lock.lock();
count++; // $ Alert
lock.unlock();
}
public void dec() {
count--;
}
}
@ThreadSafe
class FaultyTurnstileExample2 {
private Lock lock1 = new ReentrantLock();
private Lock lock2 = new ReentrantLock();
private int count = 0;
public void inc() {
lock1.lock();
count++; // $ Alert
lock1.unlock();
}
public void dec() {
lock2.lock();
count--;
lock2.unlock();
}
}

View File

@@ -0,0 +1,30 @@
package examples;
@ThreadSafe
public class FlawedSemaphore {
private final int capacity;
private int state;
public FlawedSemaphore(int c) {
capacity = c;
state = 0;
}
public void acquire() {
try {
while (state == capacity) {
this.wait();
}
state++; // $ Alert
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void release() {
synchronized (this) {
state--; // State can become negative
this.notifyAll();
}
}
}

View File

@@ -0,0 +1,51 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class LockCorrect {
private Lock lock1 = new ReentrantLock();
private int length = 0;
private int notRelatedToOther = 10;
private int[] content = new int[10];
private int thisSynchronized = 0;
public void add(int value) {
lock1.lock();
length++;
content[length] = value;
lock1.unlock();
}
public void removeCorrect() {
lock1.lock();
content[length] = 0;
length--;
lock1.unlock();
}
public void synchronizedOnLock1() {
synchronized(lock1) {
notRelatedToOther++;
}
}
public void synchronizedOnLock12() {
synchronized(lock1) {
notRelatedToOther++;
}
}
public synchronized void x() {
thisSynchronized++;
}
public void x1() {
synchronized(this) {
thisSynchronized++;
}
}
}

View File

@@ -0,0 +1,156 @@
// This example shows that we only get one alert "per concurrency problem":
// For each problematic variable, we get one alert at the earliest conflicting write.
// If the variable is involved in several different monitors, we get an alert for each monitor that
// is not correctly used.
// A single alert can have many related locations, since each conflicting access which is not
// prpoerly synchronized is a related location.
// This leads to many lines in the .expected file.
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class LockExample {
private Lock lock1 = new ReentrantLock();
private Lock lock2 = new ReentrantLock();
private int length = 0;
private int notRelatedToOther = 10;
private int[] content = new int[10];
public void add(int value) {
lock1.lock();
length++; // $ Alert
content[length] = value; // $ Alert
lock1.unlock();
}
public void removeCorrect() {
lock1.lock();
length--;
content[length] = 0;
lock1.unlock();
}
public void notTheSameLockAsAdd() { // use locks, but different ones
lock2.lock();
length--; // $ Alert
content[length] = 0; // $ Alert
lock2.unlock();
}
public void noLock() { // no locks
length--;
content[length] = 0;
}
public void fieldUpdatedOutsideOfLock() { // adjusts length without lock
length--;
lock1.lock();
content[length] = 0;
lock1.unlock();
}
public synchronized void synchronizedLock() { // no locks, but with synchronized
length--;
content[length] = 0;
}
public void onlyLocked() { // never unlocked, only locked
length--;
lock1.lock();
content[length] = 0;
}
public void onlyUnlocked() { // never locked, only unlocked
length--;
content[length] = 0;
lock1.unlock();
}
public void notSameLock() {
length--;
lock2.lock();// Not the same lock
content[length] = 0;
lock1.unlock();
}
public void updateCount() {
lock2.lock();
notRelatedToOther++; // $ Alert
lock2.unlock();
}
public void updateCountTwiceCorrect() {
lock2.lock();
notRelatedToOther++;
lock2.unlock();
lock1.lock();
notRelatedToOther++; // $ Alert
lock1.unlock();
}
public void updateCountTwiceDifferentLocks() {
lock2.lock();
notRelatedToOther++;
lock2.unlock();
lock1.lock();
notRelatedToOther++;
lock2.unlock();
}
public void updateCountTwiceLock() {
lock2.lock();
notRelatedToOther++;
lock2.unlock();
lock1.lock();
notRelatedToOther++;
}
public void updateCountTwiceUnLock() {
lock2.lock();
notRelatedToOther++;
lock2.unlock();
notRelatedToOther++;
lock1.unlock();
}
public void synchronizedNonRelatedOutside() {
notRelatedToOther++;
synchronized(this) {
length++;
}
}
public void synchronizedNonRelatedOutside2() {
int x = 0;
x++;
synchronized(this) {
length++;
}
}
public void synchronizedNonRelatedOutside3() {
synchronized(this) {
length++;
}
notRelatedToOther = 1;
}
public void synchronizedNonRelatedOutside4() {
synchronized(lock1) {
length++;
}
notRelatedToOther = 1;
}
}

View File

@@ -0,0 +1,33 @@
package examples;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class LoopyCallGraph {
private Lock lock = new ReentrantLock();
private int count = 0;
private Random random = new Random();
public void entry() {
if (random.nextBoolean()) {
increase(); // this looks like an unprotected path to a call to dec()
} else {
lock.lock();
dec();
lock.unlock();
}
}
private void increase() {
lock.lock();
count = 10; //$ SPURIOUS: Alert
lock.unlock();
entry(); // this looks like an unprotected path to a call to dec()
}
private void dec() {
count--;
}
}

View File

@@ -0,0 +1,47 @@
package examples;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class SyncLstExample<T> {
private Lock lock = new ReentrantLock();
private List<T> lst;
public SyncLstExample(List<T> lst) {
this.lst = lst;
}
public void add(T item) {
lock.lock();
lst.add(item);
lock.unlock();
}
public void remove(int i) {
lock.lock();
lst.remove(i);
lock.unlock();
}
}
@ThreadSafe
class FaultySyncLstExample<T> {
private Lock lock = new ReentrantLock();
private List<T> lst;
public FaultySyncLstExample(List<T> lst) {
this.lst = lst;
}
public void add(T item) {
lock.lock();
lst.add(item); // $ Alert
lock.unlock();
}
public void remove(int i) {
lst.remove(i);
}
}

View File

@@ -0,0 +1,39 @@
package examples;
import java.util.Stack;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class SyncStackExample<T> {
private Lock lock = new ReentrantLock();
private Stack<T> stc = new Stack<T>();
public void push(T item) {
lock.lock();
stc.push(item);
lock.unlock();
}
public void pop() {
lock.lock();
stc.pop();
lock.unlock();
}
}
@ThreadSafe
class FaultySyncStackExample<T> {
private Lock lock = new ReentrantLock();
private Stack<T> stc = new Stack<T>();
public void push(T item) {
lock.lock();
stc.push(item); // $ Alert
lock.unlock();
}
public void pop() {
stc.pop();
}
}

View File

@@ -0,0 +1,21 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class SynchronizedAndLock {
private Lock lock = new ReentrantLock();
private int length = 0;
public void add(int value) {
lock.lock();
length++; // $ Alert
lock.unlock();
}
public synchronized void subtract() {
length--;
}
}

View File

@@ -0,0 +1,76 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class Test {
/**
* Escaping field due to public visuability.
*/
int publicField;
private int y;
final int immutableField = 1;
// As of the below examples with synchronized as well. Except the incorretly placed lock.
private Lock lock = new ReentrantLock();
/**
* Calls the a method where y field escapes.
* @param y
*/
public void setYAgainInCorrect(int t) {
setYPrivate(t);
}
/**
* Locks the method where y field escapes.
* @param y
*/
public void setYAgainCorrect(int y) {
lock.lock();
setYPrivate(y);
lock.unlock();
}
/**
* No escaping y field. Locks the y before assignment.
* @param y
*/
public void setYCorrect(int y) {
lock.lock();
this.y = y; // $ Alert
lock.unlock();
}
/**
* No direct escaping, since it method is private. Only escaping if another public method uses this.
* @param y
*/
private void setYPrivate(int y) {
this.y = y;
}
/**
* Incorretly locks y.
* @param y
*/
public void setYWrongLock(int y) {
this.y = y;
lock.lock();
lock.unlock();
}
public synchronized int getImmutableField() {
return immutableField;
}
public synchronized int getImmutableField2() {
return immutableField;
}
public void testMethod() {
this.y = y + 2;
}
}

View File

@@ -0,0 +1,22 @@
package examples;
import java.util.ArrayList;
// Note: Not marked as thread-safe
// We inherit from this in Test3Super.java
public class Test2 {
int x;
protected ArrayList<String> lst = new ArrayList<>();
public Test2() {
this.x = 0;
}
public void changeX() {
this.x = x + 1;
}
public void changeLst() {
lst.add("Hello");
}
}

View File

@@ -0,0 +1,17 @@
package examples;
@ThreadSafe
public class Test3Super extends Test2 { // We might want an alert here for the inherited unsafe methods.
public Test3Super() {
super.x = 0;
}
public void y() {
super.x = 0; //$ MISSING: Alert
}
public void yLst() {
super.lst.add("Hello!"); //$ MISSING: Alert
}
}

View File

@@ -0,0 +1,4 @@
package examples;
public @interface ThreadSafe {
}

View File

@@ -0,0 +1,23 @@
package examples;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class TurnstileExample {
private Lock lock = new ReentrantLock();
private int count = 0;
public void inc() {
Lock l = lock;
l.lock();
count++;
l.unlock();
}
public void dec() {
lock.lock();
count--;
lock.unlock();
}
}