Skip to content

Commit 1ad2394

Browse files
committed
java: move shared code into Concurrency.qll
1 parent f90e9db commit 1ad2394

File tree

3 files changed

+163
-169
lines changed

3 files changed

+163
-169
lines changed

java/ql/lib/semmle/code/java/Concurrency.qll

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@ overlay[local?]
22
module;
33

44
import java
5+
import semmle.code.java.frameworks.Mockito
6+
7+
class LockType extends RefType {
8+
LockType() {
9+
this.getAMethod().hasName("lock") and
10+
this.getAMethod().hasName("unlock")
11+
}
12+
13+
Method getLockMethod() {
14+
result.getDeclaringType() = this and
15+
result.hasName(["lock", "lockInterruptibly", "tryLock"])
16+
}
17+
18+
Method getUnlockMethod() {
19+
result.getDeclaringType() = this and
20+
result.hasName("unlock")
21+
}
22+
23+
Method getIsHeldByCurrentThreadMethod() {
24+
result.getDeclaringType() = this and
25+
result.hasName("isHeldByCurrentThread")
26+
}
27+
28+
MethodCall getLockAccess() {
29+
result.getMethod() = this.getLockMethod() and
30+
// Not part of a Mockito verification call
31+
not result instanceof MockitoVerifiedMethodCall
32+
}
33+
34+
MethodCall getUnlockAccess() {
35+
result.getMethod() = this.getUnlockMethod() and
36+
// Not part of a Mockito verification call
37+
not result instanceof MockitoVerifiedMethodCall
38+
}
39+
40+
MethodCall getIsHeldByCurrentThreadAccess() {
41+
result.getMethod() = this.getIsHeldByCurrentThreadMethod() and
42+
// Not part of a Mockito verification call
43+
not result instanceof MockitoVerifiedMethodCall
44+
}
45+
}
546

647
/**
748
* Holds if `e` is synchronized by a local synchronized statement `sync` on the variable `v`.
@@ -49,3 +90,123 @@ class SynchronizedCallable extends Callable {
4990
)
5091
}
5192
}
93+
94+
/**
95+
* This module provides predicates, chiefly `locallyMonitors`, to check if a given expression is synchronized on a specific monitor.
96+
*/
97+
module Monitors {
98+
/**
99+
* A monitor is any object that is used to synchronize access to a shared resource.
100+
* This includes locks as well as variables used in synchronized blocks (including `this`).
101+
*/
102+
newtype TMonitor =
103+
/** Either a lock or a variable used in a synchronized block. */
104+
TVariableMonitor(Variable v) {
105+
v.getType() instanceof LockType or locallySynchronizedOn(_, _, v)
106+
} or
107+
/** An instance reference used as a monitor. */
108+
TInstanceMonitor(RefType thisType) { locallySynchronizedOnThis(_, thisType) } or
109+
/** A class used as a monitor. */
110+
TClassMonitor(RefType classType) { locallySynchronizedOnClass(_, classType) }
111+
112+
/**
113+
* A monitor is any object that is used to synchronize access to a shared resource.
114+
* This includes locks as well as variables used in synchronized blocks (including `this`).
115+
*/
116+
class Monitor extends TMonitor {
117+
/** Gets the location of this monitor. */
118+
abstract Location getLocation();
119+
120+
/** Gets a textual representation of this element. */
121+
abstract string toString();
122+
}
123+
124+
/**
125+
* A variable used as a monitor.
126+
* The variable is either a lock or is used in a synchronized block.
127+
* E.g `synchronized (m) { ... }` or `m.lock();`
128+
*/
129+
class VariableMonitor extends Monitor, TVariableMonitor {
130+
override Location getLocation() { result = this.getVariable().getLocation() }
131+
132+
override string toString() { result = "VariableMonitor(" + this.getVariable().toString() + ")" }
133+
134+
/** Gets the variable being used as a monitor. */
135+
Variable getVariable() { this = TVariableMonitor(result) }
136+
}
137+
138+
/**
139+
* An instance reference used as a monitor.
140+
* Either via `synchronized (this) { ... }` or by marking a non-static method as `synchronized`.
141+
*/
142+
class InstanceMonitor extends Monitor, TInstanceMonitor {
143+
override Location getLocation() { result = this.getThisType().getLocation() }
144+
145+
override string toString() { result = "InstanceMonitor(" + this.getThisType().toString() + ")" }
146+
147+
/** Gets the instance reference being used as a monitor. */
148+
RefType getThisType() { this = TInstanceMonitor(result) }
149+
}
150+
151+
/**
152+
* A class used as a monitor.
153+
* This is achieved by marking a static method as `synchronized`.
154+
*/
155+
class ClassMonitor extends Monitor, TClassMonitor {
156+
override Location getLocation() { result = this.getClassType().getLocation() }
157+
158+
override string toString() { result = "ClassMonitor(" + this.getClassType().toString() + ")" }
159+
160+
/** Gets the class being used as a monitor. */
161+
RefType getClassType() { this = TClassMonitor(result) }
162+
}
163+
164+
/** Holds if the expression `e` is synchronized on the monitor `m`. */
165+
predicate locallyMonitors(Expr e, Monitor m) {
166+
exists(Variable v | v = m.(VariableMonitor).getVariable() |
167+
locallyLockedOn(e, v)
168+
or
169+
locallySynchronizedOn(e, _, v)
170+
)
171+
or
172+
locallySynchronizedOnThis(e, m.(InstanceMonitor).getThisType())
173+
or
174+
locallySynchronizedOnClass(e, m.(ClassMonitor).getClassType())
175+
}
176+
177+
/** Holds if `localLock` refers to `lock`. */
178+
predicate represents(Field lock, Variable localLock) {
179+
lock.getType() instanceof LockType and
180+
(
181+
localLock = lock
182+
or
183+
localLock.getInitializer() = lock.getAnAccess()
184+
)
185+
}
186+
187+
/** Gets the control flow node that must dominate `e` when `e` is synchronized on a lock. */
188+
ControlFlowNode getNodeToBeDominated(Expr e) {
189+
// If `e` is the LHS of an assignment, use the control flow node for the assignment
190+
exists(Assignment asgn | asgn.getDest() = e | result = asgn.getControlFlowNode())
191+
or
192+
// if `e` is not the LHS of an assignment, use the default control flow node
193+
not exists(Assignment asgn | asgn.getDest() = e) and
194+
result = e.getControlFlowNode()
195+
}
196+
197+
/** Holds if `e` is synchronized on the `Lock` `lock` by a locking call. */
198+
predicate locallyLockedOn(Expr e, Field lock) {
199+
lock.getType() instanceof LockType and
200+
exists(Variable localLock, MethodCall lockCall, MethodCall unlockCall |
201+
represents(lock, localLock) and
202+
lockCall.getQualifier() = localLock.getAnAccess() and
203+
lockCall.getMethod() = lock.getType().(LockType).getLockMethod() and
204+
unlockCall.getQualifier() = localLock.getAnAccess() and
205+
unlockCall.getMethod() = lock.getType().(LockType).getUnlockMethod()
206+
|
207+
dominates(lockCall.getControlFlowNode(), unlockCall.getControlFlowNode()) and
208+
dominates(lockCall.getControlFlowNode(), getNodeToBeDominated(e)) and
209+
postDominates(unlockCall.getControlFlowNode(), getNodeToBeDominated(e))
210+
)
211+
}
212+
}

java/ql/lib/semmle/code/java/ConflictingAccess.qll

Lines changed: 1 addition & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -7,133 +7,6 @@ module;
77
import java
88
import Concurrency
99

10-
/**
11-
* Holds if `t` is the type of a lock.
12-
* Currently a crude test of the type name.
13-
*/
14-
bindingset[t]
15-
overlay[caller?]
16-
pragma[inline_late]
17-
predicate isLockType(Type t) { t.getName().matches("%Lock%") }
18-
19-
/**
20-
* This module provides predicates, chiefly `locallyMonitors`, to check if a given expression is synchronized on a specific monitor.
21-
*/
22-
module Monitors {
23-
/**
24-
* A monitor is any object that is used to synchronize access to a shared resource.
25-
* This includes locks as well as variables used in synchronized blocks (including `this`).
26-
*/
27-
newtype TMonitor =
28-
/** Either a lock or a variable used in a synchronized block. */
29-
TVariableMonitor(Variable v) { isLockType(v.getType()) or locallySynchronizedOn(_, _, v) } or
30-
/** An instance reference used as a monitor. */
31-
TInstanceMonitor(RefType thisType) { locallySynchronizedOnThis(_, thisType) } or
32-
/** A class used as a monitor. */
33-
TClassMonitor(RefType classType) { locallySynchronizedOnClass(_, classType) }
34-
35-
/**
36-
* A monitor is any object that is used to synchronize access to a shared resource.
37-
* This includes locks as well as variables used in synchronized blocks (including `this`).
38-
*/
39-
class Monitor extends TMonitor {
40-
/** Gets the location of this monitor. */
41-
abstract Location getLocation();
42-
43-
/** Gets a textual representation of this element. */
44-
abstract string toString();
45-
}
46-
47-
/**
48-
* A variable used as a monitor.
49-
* The variable is either a lock or is used in a synchronized block.
50-
* E.g `synchronized (m) { ... }` or `m.lock();`
51-
*/
52-
class VariableMonitor extends Monitor, TVariableMonitor {
53-
override Location getLocation() { result = this.getVariable().getLocation() }
54-
55-
override string toString() { result = "VariableMonitor(" + this.getVariable().toString() + ")" }
56-
57-
/** Gets the variable being used as a monitor. */
58-
Variable getVariable() { this = TVariableMonitor(result) }
59-
}
60-
61-
/**
62-
* An instance reference used as a monitor.
63-
* Either via `synchronized (this) { ... }` or by marking a non-static method as `synchronized`.
64-
*/
65-
class InstanceMonitor extends Monitor, TInstanceMonitor {
66-
override Location getLocation() { result = this.getThisType().getLocation() }
67-
68-
override string toString() { result = "InstanceMonitor(" + this.getThisType().toString() + ")" }
69-
70-
/** Gets the instance reference being used as a monitor. */
71-
RefType getThisType() { this = TInstanceMonitor(result) }
72-
}
73-
74-
/**
75-
* A class used as a monitor.
76-
* This is achieved by marking a static method as `synchronized`.
77-
*/
78-
class ClassMonitor extends Monitor, TClassMonitor {
79-
override Location getLocation() { result = this.getClassType().getLocation() }
80-
81-
override string toString() { result = "ClassMonitor(" + this.getClassType().toString() + ")" }
82-
83-
/** Gets the class being used as a monitor. */
84-
RefType getClassType() { this = TClassMonitor(result) }
85-
}
86-
87-
/** Holds if the expression `e` is synchronized on the monitor `m`. */
88-
predicate locallyMonitors(Expr e, Monitor m) {
89-
exists(Variable v | v = m.(VariableMonitor).getVariable() |
90-
locallyLockedOn(e, v)
91-
or
92-
locallySynchronizedOn(e, _, v)
93-
)
94-
or
95-
locallySynchronizedOnThis(e, m.(InstanceMonitor).getThisType())
96-
or
97-
locallySynchronizedOnClass(e, m.(ClassMonitor).getClassType())
98-
}
99-
100-
/** Holds if `localLock` refers to `lock`. */
101-
predicate represents(Field lock, Variable localLock) {
102-
isLockType(lock.getType()) and
103-
(
104-
localLock = lock
105-
or
106-
localLock.getInitializer() = lock.getAnAccess()
107-
)
108-
}
109-
110-
/** Gets the control flow node that must dominate `e` when `e` is synchronized on a lock. */
111-
ControlFlowNode getNodeToBeDominated(Expr e) {
112-
// If `e` is the LHS of an assignment, use the control flow node for the assignment
113-
exists(Assignment asgn | asgn.getDest() = e | result = asgn.getControlFlowNode())
114-
or
115-
// if `e` is not the LHS of an assignment, use the default control flow node
116-
not exists(Assignment asgn | asgn.getDest() = e) and
117-
result = e.getControlFlowNode()
118-
}
119-
120-
/** Holds if `e` is synchronized on the `Lock` `lock` by a locking call. */
121-
predicate locallyLockedOn(Expr e, Field lock) {
122-
isLockType(lock.getType()) and
123-
exists(Variable localLock, MethodCall lockCall, MethodCall unlockCall |
124-
represents(lock, localLock) and
125-
lockCall.getQualifier() = localLock.getAnAccess() and
126-
lockCall.getMethod().getName() in ["lock", "lockInterruptibly", "tryLock"] and
127-
unlockCall.getQualifier() = localLock.getAnAccess() and
128-
unlockCall.getMethod().getName() = "unlock"
129-
|
130-
dominates(lockCall.getControlFlowNode(), unlockCall.getControlFlowNode()) and
131-
dominates(lockCall.getControlFlowNode(), getNodeToBeDominated(e)) and
132-
postDominates(unlockCall.getControlFlowNode(), getNodeToBeDominated(e))
133-
)
134-
}
135-
}
136-
13710
/** Provides predicates, chiefly `isModifying`, to check if a given expression modifies a shared resource. */
13811
module Modification {
13912
import semmle.code.java.dataflow.FlowSummary
@@ -183,7 +56,7 @@ class ExposedField extends Field {
18356
this.getDeclaringType() instanceof ClassAnnotatedAsThreadSafe and
18457
not this.isVolatile() and
18558
// field is not a lock
186-
not isLockType(this.getType()) and
59+
not this.getType() instanceof LockType and
18760
// field is not thread-safe
18861
not isThreadSafeType(this.getType()) and
18962
not isThreadSafeType(this.getInitializer().getType()) and

java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,47 +16,7 @@
1616
import java
1717
import semmle.code.java.controlflow.Guards
1818
import semmle.code.java.dataflow.SSA
19-
import semmle.code.java.frameworks.Mockito
20-
21-
class LockType extends RefType {
22-
LockType() {
23-
this.getAMethod().hasName("lock") and
24-
this.getAMethod().hasName("unlock")
25-
}
26-
27-
Method getLockMethod() {
28-
result.getDeclaringType() = this and
29-
(result.hasName("lock") or result.hasName("tryLock"))
30-
}
31-
32-
Method getUnlockMethod() {
33-
result.getDeclaringType() = this and
34-
result.hasName("unlock")
35-
}
36-
37-
Method getIsHeldByCurrentThreadMethod() {
38-
result.getDeclaringType() = this and
39-
result.hasName("isHeldByCurrentThread")
40-
}
41-
42-
MethodCall getLockAccess() {
43-
result.getMethod() = this.getLockMethod() and
44-
// Not part of a Mockito verification call
45-
not result instanceof MockitoVerifiedMethodCall
46-
}
47-
48-
MethodCall getUnlockAccess() {
49-
result.getMethod() = this.getUnlockMethod() and
50-
// Not part of a Mockito verification call
51-
not result instanceof MockitoVerifiedMethodCall
52-
}
53-
54-
MethodCall getIsHeldByCurrentThreadAccess() {
55-
result.getMethod() = this.getIsHeldByCurrentThreadMethod() and
56-
// Not part of a Mockito verification call
57-
not result instanceof MockitoVerifiedMethodCall
58-
}
59-
}
19+
import semmle.code.java.Concurrency
6020

6121
predicate lockBlock(LockType t, BasicBlock b, int locks) {
6222
locks = strictcount(int i | b.getNode(i).asExpr() = t.getLockAccess())

0 commit comments

Comments
 (0)