3
3
* @description Checking a pointer for nullness after dereferencing it is
4
4
* likely to be a sign that either the check can be removed, or
5
5
* it should be moved before the dereference.
6
- * @kind problem
6
+ * @kind path- problem
7
7
* @problem.severity error
8
+ * @precision high
8
9
* @id cpp/redundant-null-check-simple
9
10
* @tags reliability
10
11
* correctness
11
12
* external/cwe/cwe-476
12
13
*/
13
14
14
- /*
15
- * Note: this query is not assigned a precision yet because we don't want it
16
- * to be included in query suites until its performance is well understood.
17
- */
18
-
19
15
import cpp
20
16
import semmle.code.cpp.ir.IR
21
17
import semmle.code.cpp.ir.ValueNumbering
18
+ import PathGraph
22
19
20
+ /** An instruction that represents a null pointer. */
23
21
class NullInstruction extends ConstantValueInstruction {
24
22
NullInstruction ( ) {
25
23
this .getValue ( ) = "0" and
26
24
this .getResultIRType ( ) instanceof IRAddressType
27
25
}
28
26
}
29
27
28
+ /**
29
+ * Holds if `checked` is an instruction that is checked against a null value,
30
+ * and `bool` is the instruction that represents the result of the comparison
31
+ */
30
32
predicate explicitNullTestOfInstruction ( Instruction checked , Instruction bool ) {
31
33
bool =
32
34
any ( CompareInstruction cmp |
@@ -49,14 +51,155 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
49
51
)
50
52
}
51
53
52
- pragma [ noinline ]
54
+ pragma [ nomagic ]
53
55
predicate candidateResult ( LoadInstruction checked , ValueNumber value , IRBlock dominator ) {
54
56
explicitNullTestOfInstruction ( checked , _) and
55
57
not checked .getAst ( ) .isInMacroExpansion ( ) and
56
58
value .getAnInstruction ( ) = checked and
57
59
dominator .dominates ( checked .getBlock ( ) )
58
60
}
59
61
62
+ /**
63
+ * This module constructs a pretty edges relation out of the results produced by
64
+ * the `candidateResult` predicate: We create a path using the instruction successor-
65
+ * relation from the dereference to the null check. To avoid generating very long paths,
66
+ * we compact the edges relation so that `edges(i1, i2)` only holds when `i2` is the first
67
+ * instruction that is control-flow reachable from `i1` with the same global value number
68
+ * as `i1`.
69
+ */
70
+ module PathGraph {
71
+ /**
72
+ * Holds if `deref` is a load instruction that loads a value
73
+ * from the address `address`. This predicate is restricted to
74
+ * those pairs for which we will end up reporting a result.
75
+ */
76
+ private predicate isSource ( Instruction address , LoadInstruction deref ) {
77
+ exists ( ValueNumber sourceValue |
78
+ candidateResult ( _, sourceValue , deref .getBlock ( ) ) and
79
+ sourceValue .getAnInstruction ( ) = address and
80
+ deref .getSourceAddress ( ) = address
81
+ )
82
+ }
83
+
84
+ /**
85
+ * Holds if `checked` has global value number `vn` and is an instruction that is
86
+ * used in a check against a null value.
87
+ */
88
+ private predicate isSink ( LoadInstruction checked , ValueNumber vn ) {
89
+ candidateResult ( checked , vn , _)
90
+ }
91
+
92
+ /** Holds if `i` is control-flow reachable from a relevant `LoadInstruction`. */
93
+ private predicate fwdFlow ( Instruction i ) {
94
+ isSource ( i , _)
95
+ or
96
+ exists ( Instruction mid |
97
+ fwdFlow ( mid ) and
98
+ mid .getASuccessor ( ) = i
99
+ )
100
+ }
101
+
102
+ /**
103
+ * Holds if `i` is part of a path from a relevant `LoadInstruction` to a
104
+ * check against a null value that compares a value against an instruction
105
+ * with global value number `vn`.
106
+ */
107
+ private predicate revFlow ( Instruction i , ValueNumber vn ) {
108
+ fwdFlow ( i ) and
109
+ (
110
+ isSink ( i , vn )
111
+ or
112
+ exists ( Instruction mid |
113
+ revFlow ( mid , vn ) and
114
+ i .getASuccessor ( ) = mid
115
+ )
116
+ )
117
+ }
118
+
119
+ /**
120
+ * Gets a first control-flow successor of `i` that has the same
121
+ * global value number as `i`.
122
+ */
123
+ private Instruction getASuccessor ( Instruction i ) {
124
+ exists ( ValueNumber vn |
125
+ vn .getAnInstruction ( ) = i and
126
+ result = getASuccessorWithValueNumber ( i , vn )
127
+ )
128
+ }
129
+
130
+ /**
131
+ * Gets a first control-flow successor of `i` that has the same
132
+ * global value number as `i`. Furthermore, `i` has global value
133
+ * number `vn`.
134
+ */
135
+ private Instruction getASuccessorWithValueNumber ( Instruction i , ValueNumber vn ) {
136
+ revFlow ( i , vn ) and
137
+ result = getASuccessorWithValueNumber0 ( vn , i .getASuccessor ( ) ) and
138
+ vn .getAnInstruction ( ) = i
139
+ }
140
+
141
+ pragma [ nomagic]
142
+ private Instruction getASuccessorWithValueNumber0 ( ValueNumber vn , Instruction i ) {
143
+ result = getASuccessorIfDifferentValueNumberTC ( vn , i ) and
144
+ vn .getAnInstruction ( ) = result
145
+ }
146
+
147
+ /**
148
+ * Computes the reflextive transitive closure of `getASuccessorIfDifferentValueNumber`.
149
+ */
150
+ private Instruction getASuccessorIfDifferentValueNumberTC ( ValueNumber vn , Instruction i ) {
151
+ revFlow ( i , vn ) and
152
+ (
153
+ i = result and
154
+ vn .getAnInstruction ( ) = i
155
+ or
156
+ exists ( Instruction mid |
157
+ mid = getASuccessorIfDifferentValueNumber ( vn , i ) and
158
+ result = getASuccessorIfDifferentValueNumberTC ( vn , mid )
159
+ )
160
+ )
161
+ }
162
+
163
+ /**
164
+ * Gets an instruction that is a control-flow successor of `i` and which is not assigned
165
+ * the global value number `vn`.
166
+ */
167
+ private Instruction getASuccessorIfDifferentValueNumber ( ValueNumber vn , Instruction i ) {
168
+ revFlow ( i , vn ) and
169
+ revFlow ( result , vn ) and
170
+ not vn .getAnInstruction ( ) = i and
171
+ pragma [ only_bind_into ] ( result ) = pragma [ only_bind_into ] ( i ) .getASuccessor ( )
172
+ }
173
+
174
+ query predicate nodes ( Instruction i , string key , string val ) {
175
+ revFlow ( i , _) and
176
+ key = "semmle.label" and
177
+ val = i .getAst ( ) .toString ( )
178
+ }
179
+
180
+ /**
181
+ * The control-flow successor relation, compacted by stepping
182
+ * over instruction that don't preserve the global value number.
183
+ *
184
+ * There is one exception to the above preservation rule: The
185
+ * initial step from the `LoadInstruction` (that is, the sink)
186
+ * steps to the first control-flow reachable instruction that
187
+ * has the same value number as the load instruction's address
188
+ * operand.
189
+ */
190
+ query predicate edges ( Instruction i1 , Instruction i2 ) {
191
+ getASuccessor ( i1 ) = i2
192
+ or
193
+ // We could write `isSource(i2, i1)` here, but that would
194
+ // include a not-very-informative step from `*p` to `p`.
195
+ // So we collapse `*p` -> `p` -> `q` to `*p` -> `q`.
196
+ exists ( Instruction mid |
197
+ isSource ( mid , i1 ) and
198
+ getASuccessor ( mid ) = i2
199
+ )
200
+ }
201
+ }
202
+
60
203
from LoadInstruction checked , LoadInstruction deref , ValueNumber sourceValue , IRBlock dominator
61
204
where
62
205
candidateResult ( checked , sourceValue , dominator ) and
67
210
// the pointer was null. To follow this idea to its full generality, we
68
211
// should also give an alert when `check` post-dominates `deref`.
69
212
deref .getBlock ( ) = dominator
70
- select checked , "This null check is redundant because $@ in any case." , deref ,
213
+ select checked , deref , checked , "This null check is redundant because $@ in any case." , deref ,
71
214
"the value is dereferenced"
0 commit comments