mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
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:
14
java/ql/test/query-tests/ThreadSafe/examples/App.java
Normal file
14
java/ql/test/query-tests/ThreadSafe/examples/App.java
Normal 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());
|
||||
}
|
||||
}
|
||||
72
java/ql/test/query-tests/ThreadSafe/examples/C.java
Normal file
72
java/ql/test/query-tests/ThreadSafe/examples/C.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
156
java/ql/test/query-tests/ThreadSafe/examples/LockExample.java
Normal file
156
java/ql/test/query-tests/ThreadSafe/examples/LockExample.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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--;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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--;
|
||||
}
|
||||
}
|
||||
76
java/ql/test/query-tests/ThreadSafe/examples/Test.java
Normal file
76
java/ql/test/query-tests/ThreadSafe/examples/Test.java
Normal 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;
|
||||
}
|
||||
}
|
||||
22
java/ql/test/query-tests/ThreadSafe/examples/Test2.java
Normal file
22
java/ql/test/query-tests/ThreadSafe/examples/Test2.java
Normal 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");
|
||||
}
|
||||
}
|
||||
17
java/ql/test/query-tests/ThreadSafe/examples/Test3Super.java
Normal file
17
java/ql/test/query-tests/ThreadSafe/examples/Test3Super.java
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package examples;
|
||||
|
||||
public @interface ThreadSafe {
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user