Skip to content

Commit c2d21e9

Browse files
committed
C#: Instantiate ControlFlowReachability and implement new nullness.
1 parent 449059f commit c2d21e9

File tree

3 files changed

+133
-30
lines changed

3 files changed

+133
-30
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Provides an implementation of local (intraprocedural) control flow reachability.
3+
*/
4+
5+
import csharp
6+
private import codeql.controlflow.ControlFlow
7+
private import semmle.code.csharp.controlflow.BasicBlocks
8+
private import semmle.code.csharp.controlflow.Guards as Guards
9+
private import semmle.code.csharp.ExprOrStmtParent
10+
11+
private module ControlFlowInput implements
12+
InputSig<Location, ControlFlow::Node, ControlFlow::BasicBlock>
13+
{
14+
private import csharp as CS
15+
16+
AstNode getEnclosingAstNode(ControlFlow::Node node) {
17+
node.getAstNode() = result
18+
or
19+
not exists(node.getAstNode()) and result = node.getEnclosingCallable()
20+
}
21+
22+
class AstNode = ExprOrStmtParent;
23+
24+
AstNode getParent(AstNode node) { result = node.getParent() }
25+
26+
class FinallyBlock extends AstNode {
27+
FinallyBlock() { any(TryStmt try).getFinally() = this }
28+
}
29+
30+
class Expr = CS::Expr;
31+
32+
class SourceVariable = Ssa::SourceVariable;
33+
34+
class SsaDefinition = Ssa::Definition;
35+
36+
class SsaWriteDefinition extends SsaDefinition instanceof Ssa::ExplicitDefinition {
37+
Expr getDefinition() { result = super.getADefinition().getSource() }
38+
}
39+
40+
class SsaPhiNode = Ssa::PhiNode;
41+
42+
class SsaUncertainDefinition = Ssa::UncertainDefinition;
43+
44+
class GuardValue = Guards::GuardValue;
45+
46+
predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
47+
Guards::Guards::ssaControlsBranchEdge(def, bb1, bb2, v)
48+
}
49+
50+
predicate ssaControls(SsaDefinition def, BasicBlock bb, GuardValue v) {
51+
Guards::Guards::ssaControls(def, bb, v)
52+
}
53+
54+
import Guards::Guards::InternalUtil
55+
}
56+
57+
module ControlFlowReachability = Make<Location, Cfg, ControlFlowInput>;

csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,30 @@ private import semmle.code.csharp.controlflow.Guards::AbstractValues
2626
private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
2727
private import semmle.code.csharp.frameworks.System
2828
private import semmle.code.csharp.frameworks.Test
29+
private import semmle.code.csharp.controlflow.ControlFlowReachability as Cf
30+
31+
private Expr maybeNullExpr(Expr reason) {
32+
G::Internal::nullValue(result) and reason = result
33+
or
34+
result instanceof AsExpr and reason = result
35+
or
36+
result.(AssignExpr).getRValue() = maybeNullExpr(reason)
37+
or
38+
result.(Cast).getExpr() = maybeNullExpr(reason)
39+
or
40+
result =
41+
any(ConditionalExpr ce |
42+
ce.getThen() = maybeNullExpr(reason)
43+
or
44+
ce.getElse() = maybeNullExpr(reason)
45+
)
46+
or
47+
result.(NullCoalescingExpr).getRightOperand() = maybeNullExpr(reason)
48+
}
2949

3050
/** An expression that may be `null`. */
3151
class MaybeNullExpr extends Expr {
32-
MaybeNullExpr() {
33-
G::Internal::nullValue(this)
34-
or
35-
this instanceof AsExpr
36-
or
37-
this.(AssignExpr).getRValue() instanceof MaybeNullExpr
38-
or
39-
this.(Cast).getExpr() instanceof MaybeNullExpr
40-
or
41-
this =
42-
any(ConditionalExpr ce |
43-
ce.getThen() instanceof MaybeNullExpr
44-
or
45-
ce.getElse() instanceof MaybeNullExpr
46-
)
47-
or
48-
this.(NullCoalescingExpr).getRightOperand() instanceof MaybeNullExpr
49-
}
52+
MaybeNullExpr() { this = maybeNullExpr(_) }
5053
}
5154

5255
/** An expression that is always `null`. */
@@ -123,6 +126,10 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref
123126
d = def.getAReadAtNode(bb.getNode(i))
124127
}
125128

129+
private predicate dereferenceAt(ControlFlow::Node node, Ssa::Definition def, Dereference d) {
130+
d = def.getAReadAtNode(node)
131+
}
132+
126133
/**
127134
* Holds if `e` having abstract value `vExpr` implies that SSA definition `def`
128135
* has abstract value `vDef`.
@@ -217,13 +224,16 @@ private predicate isNullDefaultArgument(Ssa::ImplicitParameterDefinition def, Al
217224
}
218225

219226
/** Holds if `def` is an SSA definition that may be `null`. */
220-
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
227+
private predicate defMaybeNull(
228+
Ssa::Definition def, ControlFlow::Node node, string msg, Element reason
229+
) {
221230
not nonNullDef(def) and
222231
(
223232
// A variable compared to `null` might be `null`
224233
exists(G::DereferenceableExpr de | de = def.getARead() |
225234
reason = de.getANullCheck(_, true) and
226235
msg = "as suggested by $@ null check" and
236+
node = def.getControlFlowNode() and
227237
not de = any(Ssa::PhiNode phi).getARead() and
228238
strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and
229239
// Don't use a check as reason if there is a `null` assignment
@@ -234,23 +244,27 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
234244
or
235245
// A parameter might be `null` if there is a `null` argument somewhere
236246
isMaybeNullArgument(def, reason) and
247+
node = def.getControlFlowNode() and
237248
(
238249
if reason instanceof AlwaysNullExpr
239250
then msg = "because of $@ null argument"
240251
else msg = "because of $@ potential null argument"
241252
)
242253
or
243-
isNullDefaultArgument(def, reason) and msg = "because the parameter has a null default value"
254+
isNullDefaultArgument(def, reason) and
255+
node = def.getControlFlowNode() and
256+
msg = "because the parameter has a null default value"
244257
or
245258
// If the source of a variable is `null` then the variable may be `null`
246259
exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() |
247-
adef.getSource() instanceof MaybeNullExpr and
260+
adef.getSource() = maybeNullExpr(node.getAstNode()) and
248261
reason = adef.getExpr() and
249262
msg = "because of $@ assignment"
250263
)
251264
or
252265
// A variable of nullable type may be null
253266
exists(Dereference d | dereferenceAt(_, _, def, d) |
267+
node = def.getControlFlowNode() and
254268
d.hasNullableType() and
255269
not def instanceof Ssa::PhiNode and
256270
reason = def.getSourceVariable().getAssignable() and
@@ -261,7 +275,7 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
261275

262276
pragma[noinline]
263277
private predicate sourceVariableMaybeNull(Ssa::SourceVariable v) {
264-
defMaybeNull(v.getAnSsaDefinition(), _, _)
278+
defMaybeNull(v.getAnSsaDefinition(), _, _, _)
265279
}
266280

267281
pragma[noinline]
@@ -305,7 +319,7 @@ private predicate defNullImpliesStep(
305319
* That is, those basic blocks for which the SSA definition is suspected of being `null`.
306320
*/
307321
private predicate defMaybeNullInBlock(Ssa::Definition def, BasicBlock bb) {
308-
defMaybeNull(def, _, _) and
322+
defMaybeNull(def, _, _, _) and
309323
bb = def.getBasicBlock()
310324
or
311325
exists(BasicBlock mid, Ssa::Definition midDef | defMaybeNullInBlock(midDef, mid) |
@@ -341,7 +355,7 @@ private predicate succSourceSink(SourcePathNode source, Ssa::Definition def, Bas
341355
private newtype TPathNode =
342356
TSourcePathNode(Ssa::Definition def, string msg, Element reason, boolean isNullArgument) {
343357
nullDerefCandidateVariable(def.getSourceVariable()) and
344-
defMaybeNull(def, msg, reason) and
358+
defMaybeNull(def, _, msg, reason) and
345359
if isMaybeNullArgument(def, reason) then isNullArgument = true else isNullArgument = false
346360
} or
347361
TInternalPathNode(Ssa::Definition def, BasicBlock bb) {
@@ -503,6 +517,40 @@ private predicate defReaches(Ssa::Definition def, ControlFlow::Node cfn, boolean
503517
)
504518
}
505519

520+
private module NullnessConfig implements Cf::ControlFlowReachability::ConfigSig {
521+
predicate source(ControlFlow::Node node, Ssa::Definition def) { defMaybeNull(def, node, _, _) }
522+
523+
predicate sink(ControlFlow::Node node, Ssa::Definition def) {
524+
exists(Dereference d |
525+
dereferenceAt(node, def, d) and
526+
not d instanceof NonNullExpr
527+
)
528+
}
529+
530+
predicate barrierValue(G::GuardValue gv) { gv.isNullness(false) }
531+
532+
predicate uncertainFlow() { none() }
533+
}
534+
535+
private module NullnessFlow = Cf::ControlFlowReachability::Flow<NullnessConfig>;
536+
537+
predicate debug_nullness_new(Dereference d, Ssa::SourceVariable v, string msg, Element reason) {
538+
exists(
539+
Ssa::Definition origin, Ssa::Definition ssa, ControlFlow::Node src, ControlFlow::Node sink
540+
|
541+
defMaybeNull(origin, src, msg, reason) and
542+
NullnessFlow::flow(src, origin, sink, ssa) and
543+
ssa.getSourceVariable() = v and
544+
dereferenceAt(sink, ssa, d) and
545+
not d.isAlwaysNull(v)
546+
)
547+
}
548+
549+
predicate debug_nullness_orig(Dereference d, Ssa::SourceVariable v, string msg, Element reason) {
550+
d.isFirstMaybeNull(v.getAnSsaDefinition(), _, _, msg, reason) and
551+
not d instanceof NonNullExpr
552+
}
553+
506554
/**
507555
* An expression that dereferences a value. That is, an expression that may
508556
* result in a `NullReferenceException` if the value is `null`.

csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,11 @@ module Ssa {
163163
* (`ImplicitDefinition`), or a phi node (`PhiNode`).
164164
*/
165165
class Definition extends SsaImpl::Definition {
166-
/**
167-
* Gets the control flow node of this SSA definition, if any. Phi nodes are
168-
* examples of SSA definitions without a control flow node, as they are
169-
* modeled at index `-1` in the relevant basic block.
170-
*/
166+
/** Gets the control flow node of this SSA definition. */
171167
final ControlFlow::Node getControlFlowNode() {
172-
exists(ControlFlow::BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i))
168+
exists(ControlFlow::BasicBlock bb, int i | this.definesAt(_, bb, i) |
169+
result = bb.getNode(0.maximum(i))
170+
)
173171
}
174172

175173
/**

0 commit comments

Comments
 (0)