33 * @description Checking a pointer for nullness after dereferencing it is
44 * likely to be a sign that either the check can be removed, or
55 * it should be moved before the dereference.
6- * @kind problem
6+ * @kind path- problem
77 * @problem.severity error
8+ * @precision high
89 * @id cpp/redundant-null-check-simple
910 * @tags reliability
1011 * correctness
1112 * external/cwe/cwe-476
1213 */
1314
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-
1915import cpp
2016import semmle.code.cpp.ir.IR
2117import semmle.code.cpp.ir.ValueNumbering
18+ import PathGraph
2219
20+ /** An instruction that represents a null pointer. */
2321class NullInstruction extends ConstantValueInstruction {
2422 NullInstruction ( ) {
2523 this .getValue ( ) = "0" and
2624 this .getResultIRType ( ) instanceof IRAddressType
2725 }
2826}
2927
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+ */
3032predicate explicitNullTestOfInstruction ( Instruction checked , Instruction bool ) {
3133 bool =
3234 any ( CompareInstruction cmp |
@@ -49,14 +51,155 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
4951 )
5052}
5153
52- pragma [ noinline ]
54+ pragma [ nomagic ]
5355predicate candidateResult ( LoadInstruction checked , ValueNumber value , IRBlock dominator ) {
5456 explicitNullTestOfInstruction ( checked , _) and
5557 not checked .getAst ( ) .isInMacroExpansion ( ) and
5658 value .getAnInstruction ( ) = checked and
5759 dominator .dominates ( checked .getBlock ( ) )
5860}
5961
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+
60203from LoadInstruction checked , LoadInstruction deref , ValueNumber sourceValue , IRBlock dominator
61204where
62205 candidateResult ( checked , sourceValue , dominator ) and
67210 // the pointer was null. To follow this idea to its full generality, we
68211 // should also give an alert when `check` post-dominates `deref`.
69212 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 ,
71214 "the value is dereferenced"
0 commit comments