-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathSynchronization.qll
More file actions
195 lines (172 loc) · 5.48 KB
/
Synchronization.qll
File metadata and controls
195 lines (172 loc) · 5.48 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/**
* Utilities for analyzing synchronization primitives, such
* as mutexes and semaphores.
*/
import cpp
/**
* A type that acts as a mutex. This class is extended below and and may
* be extended in `Options.qll`.
*/
abstract class MutexType extends Type {
/**
* Holds if `fc` is a call that always locks mutex `arg`
* of this type.
*/
abstract predicate mustlockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is a call that tries to lock mutex `arg`
* of this type, but may return without success.
*/
abstract predicate trylockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is a call that unlocks mutex `arg` of this type.
*/
abstract predicate unlockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is n call that locks or tries to lock mutex
* `arg` of this type.
*/
predicate lockAccess(FunctionCall fc, Expr arg) {
this.mustlockAccess(fc, arg) or
this.trylockAccess(fc, arg)
}
/**
* Gets a call that locks or tries to lock any mutex of this type.
*/
FunctionCall getLockAccess() {
result = this.getMustlockAccess() or
result = this.getTrylockAccess()
}
/**
* Gets a call that always locks any mutex of this type.
*/
FunctionCall getMustlockAccess() { this.mustlockAccess(result, _) }
/**
* Gets a call that tries to lock any mutex of this type,
* by may return without success.
*/
FunctionCall getTrylockAccess() { this.trylockAccess(result, _) }
/**
* Gets a call that unlocks any mutex of this type.
*/
FunctionCall getUnlockAccess() { this.unlockAccess(result, _) }
}
/**
* Gets a function that looks like a lock function.
*/
private Function mustlockCandidate() {
exists(string name | name = result.getName() |
name = "lock" or
name.matches("%mutex\\_lock")
)
}
/**
* Gets a function that looks like a try-lock function.
*/
private Function trylockCandidate() {
exists(string name | name = result.getName() |
name = "try_lock" or
name.matches("%mutex\\_trylock")
)
}
/**
* Gets a function that looks like an unlock function.
*/
private Function unlockCandidate() {
exists(string name | name = result.getName() |
name = "unlock" or
name.matches("%mutex\\_unlock")
)
}
/**
* Gets a type that is a parameter to a function, or it's declaring type
* (i.e. it's `this`). If the function is a locking related function,
* these can be thought of as candidates for the mutex is it locking or
* unlocking. It is narrowed down in `DefaultMutexType` by requiring that
* it must be a class type with both a lock and an unlock function.
*/
private Class lockArgTypeCandidate(Function fcn) {
result = fcn.getDeclaringType() or
result = fcn.getAParameter().getType().stripType()
}
/**
* A class or struct type that has both a lock and an unlock function
* candidate, and is therefore a mutex.
*
* This excludes types like `std::weak_ptr` which has a lock
* method, but not an unlock method, and is not a mutex.)
*/
class DefaultMutexType extends MutexType {
DefaultMutexType() {
this = lockArgTypeCandidate(mustlockCandidate()) and
this = lockArgTypeCandidate(unlockCandidate())
}
private predicate lockArgType(FunctionCall fc, Expr arg) {
exists(int n |
arg = fc.getArgument(n) and
fc.getTarget().getParameter(n).getType().stripType() = this
)
or
fc.getTarget().getDeclaringType() = this and
arg = fc.getQualifier()
or
// if we're calling our own method with an implicit `this`,
// let `arg` be the function call, since we don't really have
// anything else to use.
fc.getTarget().getDeclaringType() = this and
not exists(fc.getQualifier()) and
arg = fc
}
override predicate mustlockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = mustlockCandidate() and
this.lockArgType(fc, arg)
}
override predicate trylockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = trylockCandidate() and
this.lockArgType(fc, arg)
}
override predicate unlockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = unlockCandidate() and
this.lockArgType(fc, arg)
}
}
/**
* Holds if `arg` is the mutex argument of a call to lock or unlock and
* `argType` is the type of the mutex.
*/
private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) {
argType = arg.getUnderlyingType().stripType() and
(
arg = call.getQualifier() or
arg = call.getAnArgument()
)
// note: this seems to arbitrarily care about argument types, rather
// than parameter types as elsewhere. As a result `mustlockCall`,
// for example, has slightly different results from
// `MutexType.mustlockAccess`.
}
/**
* Holds if `call` is a call that locks or tries to lock its argument `arg`.
*/
predicate lockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess())
}
/**
* Holds if `call` is a call that always locks its argument `arg`.
*/
predicate mustlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess())
}
/**
* Holds if `call` is a call that tries to lock its argument `arg`, but may
* return without success.
*/
predicate trylockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess())
}
/**
* Holds if `call` is a call that unlocks its argument `arg`.
*/
predicate unlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getUnlockAccess())
}