-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathNullness.qll
More file actions
285 lines (267 loc) · 7.27 KB
/
Nullness.qll
File metadata and controls
285 lines (267 loc) · 7.27 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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/**
* Provides classes and predicates for working with null values and checks for nullness.
*/
import cpp
import DefinitionsAndUses
/**
* A C/C++ literal whose value is considered null.
*/
abstract class NullValue extends Expr { }
/**
* A C/C++ literal whose value is zero.
*/
class Zero extends NullValue {
Zero() { this.(Literal).getValue() = "0" }
}
/**
* Holds if `var` is null when `checkExpr` evaluates to a true value.
*/
cached
predicate nullCheckExpr(Expr checkExpr, Variable var) {
exists(LocalScopeVariable v, AnalysedExpr expr |
var = v and
checkExpr = expr
|
exists(NotExpr notexpr, AnalysedExpr child |
expr = notexpr and notexpr.getOperand() = child and validCheckExpr(child, v)
)
or
exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i |
expr = eq and
eq.getChild(i) = child and
validCheckExpr(child, v) and
eq.getChild(1 - i) = zero
)
or
exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i |
expr = neq and
neq.getChild(i) = child and
nullCheckExpr(child, v) and
neq.getChild(1 - i) = zero
)
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
op.getAnOperand() = child and
nullCheckExpr(child, v)
)
or
exists(AssignExpr assign, AnalysedExpr child |
expr = assign and
assign.getRValue() = child and
nullCheckExpr(child, v) and
not expr.isDef(v)
)
or
exists(FunctionCall fc, AnalysedExpr child |
expr = fc and
fc.getTarget().hasGlobalName("__builtin_expect") and
fc.getArgument(0) = child and
nullCheckExpr(child, v)
)
)
}
/**
* Holds if `var` is non-null when `checkExpr` evaluates to a true value.
*/
cached
predicate validCheckExpr(Expr checkExpr, Variable var) {
exists(AnalysedExpr expr, LocalScopeVariable v |
v = var and
expr = checkExpr
|
expr.isUse(v)
or
expr.isDef(v)
or
exists(NotExpr notexpr, AnalysedExpr child |
expr = notexpr and notexpr.getOperand() = child and nullCheckExpr(child, v)
)
or
exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i |
expr = eq and
eq.getChild(i) = child and
nullCheckExpr(child, v) and
eq.getChild(1 - i) = zero
)
or
exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i |
expr = neq and
neq.getChild(i) = child and
validCheckExpr(child, v) and
neq.getChild(1 - i) = zero
)
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
op.getAnOperand() = child and
validCheckExpr(child, v)
)
or
exists(AssignExpr assign, AnalysedExpr child |
expr = assign and
assign.getRValue() = child and
validCheckExpr(child, v) and
not expr.isDef(v)
)
or
exists(FunctionCall fc, AnalysedExpr child |
expr = fc and
fc.getTarget().hasGlobalName("__builtin_expect") and
fc.getArgument(0) = child and
validCheckExpr(child, v)
)
)
}
/**
* An expression that has been extended with member predicates that provide
* information about the role of this expression in nullness checks.
*/
class AnalysedExpr extends Expr {
/**
* Holds if `v` is null when this expression evaluates to a true value.
*/
predicate isNullCheck(LocalScopeVariable v) { nullCheckExpr(this, v) }
/**
* Holds if `v` is non-null when this expression evaluates to a true value.
*/
predicate isValidCheck(LocalScopeVariable v) { validCheckExpr(this, v) }
/**
* Gets a successor of `this` in the control flow graph, where that successor
* is among the nodes to which control may flow when `this` tests `v` to be
* _null_.
*/
ControlFlowNode getNullSuccessor(LocalScopeVariable v) {
this.isNullCheck(v) and result = this.getATrueSuccessor()
or
this.isValidCheck(v) and result = this.getAFalseSuccessor()
}
/**
* Gets a successor of `this` in the control flow graph, where that successor
* is among the nodes to which control may flow when `this` tests `v` to be
* _not null_.
*/
ControlFlowNode getNonNullSuccessor(LocalScopeVariable v) {
this.isNullCheck(v) and result = this.getAFalseSuccessor()
or
this.isValidCheck(v) and result = this.getATrueSuccessor()
}
/**
* Holds if this is a `VariableAccess` of `v` nested inside a condition.
*/
predicate isUse(LocalScopeVariable v) {
this.inCondition() and
this = v.getAnAccess()
}
/**
* Holds if this is an `Assignment` to `v` nested inside a condition.
*/
predicate isDef(LocalScopeVariable v) {
this.inCondition() and
(
this.(Assignment).getLValue() = v.getAnAccess() or
this.(ConditionDeclExpr).getVariableAccess() = v.getAnAccess()
)
}
/**
* Holds if `this` occurs at or below the controlling expression of an `if`,
* `while`, `?:`, or similar.
*/
predicate inCondition() {
this.isCondition() or
this.getParent().(AnalysedExpr).inCondition()
}
}
/**
* Holds if `var` is likely to be null at `node`.
*/
cached
predicate checkedNull(Variable var, ControlFlowNode node) {
exists(LocalScopeVariable v | v = var |
exists(AnalysedExpr e | e.getNullSuccessor(v) = node)
or
exists(ControlFlowNode pred |
pred = node.getAPredecessor() and
not pred.(AnalysedExpr).isDef(v) and
checkedNull(v, pred)
)
)
}
/**
* Holds if `var` is likely to be non-null at `node`.
*/
cached
predicate checkedValid(Variable var, ControlFlowNode node) {
exists(LocalScopeVariable v | v = var |
exists(AnalysedExpr e | e.getNonNullSuccessor(v) = node)
or
exists(ControlFlowNode pred |
pred = node.getAPredecessor() and
not pred.(AnalysedExpr).isDef(v) and
checkedValid(v, pred)
)
)
}
/**
* Holds if `val` is a null literal or a call to a function that may return a
* null literal.
*/
predicate nullValue(Expr val) {
val instanceof NullValue
or
callMayReturnNull(val)
or
nullValue(val.(Assignment).getRValue())
}
/**
* Holds if the evaluation of `n` may have the effect of, directly or
* indirectly, assigning a null literal to `var`.
*/
predicate nullInit(Variable v, ControlFlowNode n) {
exists(Initializer init |
init = n and
nullValue(init.getExpr()) and
v.getInitializer() = init
)
or
exists(AssignExpr assign |
assign = n and
nullValue(assign.getRValue()) and
assign.getLValue() = v.getAnAccess()
)
}
/**
* Holds if `call` may, directly or indirectly, evaluate to a null literal.
*/
predicate callMayReturnNull(Call call) {
exists(Options opts |
if opts.overrideReturnsNull(call)
then opts.returnsNull(call)
else mayReturnNull(call.(FunctionCall).getTarget())
)
}
/**
* Holds if `f` may, directly or indirectly, return a null literal.
*/
predicate mayReturnNull(Function f) {
f.hasGlobalOrStdName("malloc")
or
f.hasGlobalOrStdName("calloc")
or
// f.hasGlobalName("strchr")
// or
// f.hasGlobalName("strstr")
// or
exists(ReturnStmt ret |
nullValue(ret.getExpr()) and
ret.getEnclosingFunction() = f
)
or
exists(ReturnStmt ret, Expr returned, ControlFlowNode init, LocalScopeVariable v |
ret.getExpr() = returned and
nullInit(v, init) and
definitionUsePair(v, init, returned) and
not checkedValid(v, returned) and
ret.getEnclosingFunction() = f
)
}