-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathNotifyWithoutSynch.ql
More file actions
139 lines (131 loc) · 3.66 KB
/
NotifyWithoutSynch.ql
File metadata and controls
139 lines (131 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* @name Notify on unlocked object
* @description Calling 'wait', 'notify', or 'notifyAll' on an object which has not
* been locked (with a synchronized method or statement) will throw.
* @kind problem
* @problem.severity error
* @precision low
* @id java/notify-without-sync
* @tags correctness
* concurrency
* language-features
*/
import java
/** The set of methods to test: `wait`, `notify`, `notifyAll`. */
class MethodRequiresSynch extends Method {
MethodRequiresSynch() {
(
this.hasName("wait") or
this.hasName("notify") or
this.hasName("notifyAll")
)
and
this.getDeclaringType().hasQualifiedName("java.lang","Object")
}
}
/**
* Auxiliary predicate for `synchronizedThisAccess`. It holds if `c`
* has a `synchronized` modifier or if `c` is private and all of
* its call sites are from an appropriate synchronization context.
*
* This means that the following example is OK:
*
* ```
* void foo() {
* synchronized(x) {
* bar();
* }
* }
*
* void bar() {
* x.notify()
* }
* ```
*/
private
predicate synchronizedCallable(Callable c) {
c.isSynchronized() or
(
c.isPrivate() and
forall(MethodAccess parent | parent.getCallee() = c |
synchronizedThisAccess(parent, c.getDeclaringType())
)
)
}
/**
* Auxiliary predicate for `unsynchronizedExplicitThisAccess` and
* `unsynchronizedImplicitThisAccess`. It holds if there is an
* enclosing synchronization context of the appropriate type. For
* example, if the method call is `MyClass.wait()`, then the predicate
* holds if there is an enclosing synchronization on `MyClass.this`.
*/
private
predicate synchronizedThisAccess(MethodAccess ma, Type thisType) {
// Are we inside a synchronized method?
exists(Callable c |
c = ma.getEnclosingCallable() and
c.getDeclaringType() = thisType and
synchronizedCallable(c)
) or
// Is there an enclosing `synchronized` statement?
exists(SynchronizedStmt s, ThisAccess x |
s.getAChild*() = ma.getEnclosingStmt() and
s.getExpr() = x and
x.getType() = thisType
)
}
/**
* Auxiliary predicate for `unsynchronizedVarAccess`. Holds if
* there is an enclosing `synchronized` statement on the variable.
*/
predicate synchronizedVarAccess(VarAccess x) {
exists(SynchronizedStmt s, VarAccess y |
s.getAChild*() = x.getEnclosingStmt() and
s.getExpr() = y and
y.getVariable() = x.getVariable() and
y.toString() = x.toString()
)
}
/**
* This predicate holds if the `MethodAccess` is a qualified call,
* such as `this.wait()`, and it is not inside a synchronized statement
* or method.
*/
private
predicate unsynchronizedExplicitThisAccess(MethodAccess ma) {
exists(ThisAccess x |
x = ma.getQualifier() and
not synchronizedThisAccess(ma, x.getType())
)
}
/**
* Holds if the `MethodAccess` is an unqualified call,
* such as `wait()`, and it is not inside a synchronized statement
* or method.
*/
private
predicate unsynchronizedImplicitThisAccess(MethodAccess ma) {
not ma.hasQualifier() and
not synchronizedThisAccess(ma, ma.getEnclosingCallable().getDeclaringType())
}
/**
* Holds if the `MethodAccess` is on a variable,
* such as `x.wait()`, and it is not inside a synchronized statement.
*/
private
predicate unsynchronizedVarAccess(MethodAccess ma) {
exists(VarAccess x |
x = ma.getQualifier() and
not synchronizedVarAccess(x)
)
}
from MethodAccess ma, Method m
where
m = ma.getMethod() and
m instanceof MethodRequiresSynch and
(
unsynchronizedExplicitThisAccess(ma) or
unsynchronizedImplicitThisAccess(ma) or
unsynchronizedVarAccess(ma)
)
select ma, "Calling " + m.getName() + " on an unsynchronized object."