Skip to content

Commit 1710f8d

Browse files
authored
Merge pull request #661 from hvitved/csharp/maybe-null-path-query
C#: Make `cs/dereferenced-value-may-be-null` a path query
2 parents 83ccddf + b2500a0 commit 1710f8d

File tree

11 files changed

+1155
-124
lines changed

11 files changed

+1155
-124
lines changed

csharp/ql/src/CSI/NullMaybe.ql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @name Dereferenced variable may be null
33
* @description Dereferencing a variable whose value may be 'null' may cause a
44
* 'NullReferenceException'.
5-
* @kind problem
5+
* @kind path-problem
66
* @problem.severity warning
77
* @precision high
88
* @id cs/dereferenced-value-may-be-null
@@ -14,7 +14,8 @@
1414

1515
import csharp
1616
import semmle.code.csharp.dataflow.Nullness
17+
import PathGraph
1718

18-
from Dereference d, Ssa::SourceVariable v, string msg, Element reason
19-
where d.isFirstMaybeNull(v.getAnSsaDefinition(), msg, reason)
20-
select d, "Variable $@ may be null here " + msg + ".", v, v.toString(), reason, "this"
19+
from Dereference d, PathNode source, PathNode sink, Ssa::SourceVariable v, string msg, Element reason
20+
where d.isFirstMaybeNull(v.getAnSsaDefinition(), source, sink, msg, reason)
21+
select d, source, sink, "Variable $@ may be null here " + msg + ".", v, v.toString(), reason, "this"

csharp/ql/src/semmle/code/csharp/Assignable.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,12 @@ class AssignableDefinition extends TAssignableDefinition {
441441
*/
442442
Expr getExpr() { none() }
443443

444+
/**
445+
* Gets the underlying element associated with this definition. This is either
446+
* an expression or a parameter.
447+
*/
448+
Element getElement() { result = this.getExpr() }
449+
444450
/** Gets the enclosing callable of this definition. */
445451
Callable getEnclosingCallable() { result = this.getExpr().getEnclosingCallable() }
446452

@@ -703,6 +709,8 @@ module AssignableDefinitions {
703709
result = p.getCallable().getEntryPoint()
704710
}
705711

712+
override Parameter getElement() { result = p }
713+
706714
override Callable getEnclosingCallable() {
707715
result = p.getCallable()
708716
}

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,12 @@ module Internal {
578578
*/
579579
Expr getNullEquivParent(Expr e) {
580580
result = any(QualifiableExpr qe |
581-
qe.getQualifier() = e and
582581
qe.isConditional() and
582+
(
583+
e = qe.getQualifier()
584+
or
585+
e = qe.(ExtensionMethodCall).getArgument(0)
586+
) and
583587
(
584588
// The accessed declaration must have a value type in order
585589
// for `only if` to hold
@@ -690,7 +694,7 @@ module Internal {
690694
predicate preControls(PreBasicBlocks::PreBasicBlock bb, AbstractValue v) {
691695
exists(AbstractValue v0, Guard g |
692696
g.preControlsDirect(bb, v0) |
693-
impliesSteps(g, v0, this, v)
697+
preImpliesSteps(g, v0, this, v)
694698
)
695699
}
696700

@@ -777,7 +781,7 @@ module Internal {
777781
def.nullGuardedReturn(ret, isNull)
778782
or
779783
exists(NullValue nv |
780-
impliesSteps(ret, retVal, def.getARead(), nv) |
784+
preImpliesSteps(ret, retVal, def.getARead(), nv) |
781785
if nv.isNull() then isNull = true else isNull = false
782786
)
783787
)
@@ -1119,6 +1123,28 @@ module Internal {
11191123
def = guarded.getAnSsaQualifier()
11201124
)
11211125
}
1126+
1127+
/**
1128+
* Holds if the assumption that `g1` has abstract value `v1` implies that
1129+
* `g2` has abstract value `v2`, using one step of reasoning. That is, the
1130+
* evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`.
1131+
*
1132+
* This predicate relies on the control flow graph.
1133+
*/
1134+
cached
1135+
predicate impliesStep(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
1136+
preImpliesStep(g1, v1, g2, v2)
1137+
or
1138+
forex(ControlFlow::Node cfn |
1139+
cfn = g1.getAControlFlowNode() |
1140+
exists(Ssa::ExplicitDefinition def |
1141+
def.getADefinition().getSource() = g2 |
1142+
g1 = def.getAReadAtNode(cfn) and
1143+
v1 = g1.getAValue() and
1144+
v2 = v1
1145+
)
1146+
)
1147+
}
11221148
}
11231149
import Cached
11241150

@@ -1139,9 +1165,11 @@ module Internal {
11391165
* Holds if the assumption that `g1` has abstract value `v1` implies that
11401166
* `g2` has abstract value `v2`, using one step of reasoning. That is, the
11411167
* evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`.
1168+
*
1169+
* This predicate does not rely on the control flow graph.
11421170
*/
11431171
cached
1144-
predicate impliesStep(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
1172+
predicate preImpliesStep(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
11451173
g1 = any(BinaryOperation bo |
11461174
(
11471175
bo instanceof BitwiseAndExpr or
@@ -1275,6 +1303,26 @@ module Internal {
12751303
* Holds if the assumption that `g1` has abstract value `v1` implies that
12761304
* `g2` has abstract value `v2`, using zero or more steps of reasoning. That is,
12771305
* the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`.
1306+
*
1307+
* This predicate does not rely on the control flow graph.
1308+
*/
1309+
predicate preImpliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
1310+
g1 = g2 and
1311+
v1 = v2 and
1312+
v1 = g1.getAValue()
1313+
or
1314+
exists(Expr mid, AbstractValue vMid |
1315+
preImpliesSteps(g1, v1, mid, vMid) |
1316+
preImpliesStep(mid, vMid, g2, v2)
1317+
)
1318+
}
1319+
1320+
/**
1321+
* Holds if the assumption that `g1` has abstract value `v1` implies that
1322+
* `g2` has abstract value `v2`, using zero or more steps of reasoning. That is,
1323+
* the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`.
1324+
*
1325+
* This predicate relies on the control flow graph.
12781326
*/
12791327
predicate impliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) {
12801328
g1 = g2 and

0 commit comments

Comments
 (0)