Skip to content

Commit eafb6d8

Browse files
authored
Merge pull request #1 from hvitved/cs/non-null-functions
C#: Adjustments to CIL/nullness analyses
2 parents 449e65d + 6c18256 commit eafb6d8

File tree

9 files changed

+84
-50
lines changed

9 files changed

+84
-50
lines changed

csharp/ql/src/semmle/code/cil/CallableReturns.qll

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,41 @@
44

55
private import CIL
66

7-
/** Holds if method `m` always returns null. */
87
cached
9-
predicate alwaysNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNullExpr(e)) }
8+
private module Cached {
9+
/** Holds if method `m` always returns null. */
10+
cached
11+
predicate alwaysNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNullExpr(e)) }
1012

11-
/** Holds if method `m` always returns non-null. */
12-
cached
13-
predicate alwaysNotNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNotNullExpr(e)) }
13+
/** Holds if method `m` always returns non-null. */
14+
cached
15+
predicate alwaysNotNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNotNullExpr(e)) }
16+
17+
/** Holds if method `m` always throws an exception. */
18+
cached
19+
predicate alwaysThrowsMethod(Method m) {
20+
m.hasBody() and
21+
not exists(m.getImplementation().getAnInstruction().(Return))
22+
}
23+
24+
/** Holds if method `m` always throws an exception of type `t`. */
25+
cached
26+
predicate alwaysThrowsException(Method m, Type t) {
27+
alwaysThrowsMethod(m) and
28+
forex(Throw ex | ex = m.getImplementation().getAnInstruction() | t = ex.getExpr().getType())
29+
}
30+
}
31+
import Cached
1432

1533
/** Holds if expression `expr` always evaluates to `null`. */
1634
private predicate alwaysNullExpr(Expr expr) {
1735
expr instanceof NullLiteral
1836
or
1937
alwaysNullMethod(expr.(StaticCall).getTarget())
2038
or
21-
forex(VariableUpdate vu | defUse(_, vu, expr) | alwaysNullExpr(vu.getSource()))
39+
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) |
40+
alwaysNullExpr(vu.getSource())
41+
)
2242
}
2343

2444
/** Holds if expression `expr` always evaluates to non-null. */
@@ -29,19 +49,7 @@ private predicate alwaysNotNullExpr(Expr expr) {
2949
or
3050
alwaysNotNullMethod(expr.(StaticCall).getTarget())
3151
or
32-
forex(VariableUpdate vu | defUse(_, vu, expr) | alwaysNotNullExpr(vu.getSource()))
33-
}
34-
35-
/** Holds if method `m` always throws an exception. */
36-
cached
37-
predicate alwaysThrowsMethod(Method m) {
38-
m.hasBody() and
39-
not exists(m.getImplementation().getAnInstruction().(Return))
40-
}
41-
42-
/** Holds if method `m` always throws an exception of type `t`. */
43-
cached
44-
predicate alwaysThrowsException(Method m, Type t) {
45-
alwaysThrowsMethod(m) and
46-
forex(Throw ex | ex = m.getImplementation().getAnInstruction() | t = ex.getExpr().getType())
52+
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) |
53+
alwaysNotNullExpr(vu.getSource())
54+
)
4755
}

csharp/ql/src/semmle/code/cil/DataFlow.qll

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
8888
}
8989

9090
cached
91-
private module DefUse {
91+
module DefUse {
9292
/**
9393
* A classification of variable references into reads and writes.
9494
*/
@@ -185,21 +185,26 @@ private module DefUse {
185185
)
186186
}
187187

188-
/** Holds if the update `def` can be used at the read `use`. */
188+
/** Holds if the variable update `vu` can be used at the read `use`. */
189189
cached
190-
predicate defUseImpl(StackVariable target, DataFlowNode def, ReadAccess use) {
191-
exists(VariableUpdate vu | def = vu.getSource() |
192-
defReachesReadWithinBlock(target, vu, use)
193-
or
194-
exists(BasicBlock bb, int i |
195-
exists(refRank(bb, i, target, Read())) and
196-
use = bb.getNode(i) and
197-
defReachesEndOfBlock(bb.getAPredecessor(), vu, target) and
198-
not defReachesReadWithinBlock(target, _, use)
199-
)
190+
predicate variableUpdateUse(StackVariable target, VariableUpdate vu, ReadAccess use) {
191+
defReachesReadWithinBlock(target, vu, use)
192+
or
193+
exists(BasicBlock bb, int i |
194+
exists(refRank(bb, i, target, Read())) and
195+
use = bb.getNode(i) and
196+
defReachesEndOfBlock(bb.getAPredecessor(), vu, target) and
197+
not defReachesReadWithinBlock(target, _, use)
200198
)
201199
}
200+
201+
/** Holds if the update `def` can be used at the read `use`. */
202+
cached
203+
predicate defUse(StackVariable target, Expr def, ReadAccess use) {
204+
exists(VariableUpdate vu | def = vu.getSource() | variableUpdateUse(target, vu, use))
205+
}
202206
}
207+
private import DefUse
203208

204209
abstract library class VariableUpdate extends Instruction {
205210
abstract Expr getSource();
@@ -224,5 +229,3 @@ private class MethodOutOrRefTarget extends VariableUpdate, Call {
224229

225230
override Expr getSource() { result = this }
226231
}
227-
228-
predicate defUse = DefUse::defUseImpl/3;

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44

55
import csharp
6+
private import cil
7+
private import dotnet
68
private import ControlFlow::SuccessorTypes
79
private import semmle.code.csharp.commons.Assertions
810
private import semmle.code.csharp.commons.ComparisonTest
@@ -11,6 +13,7 @@ private import semmle.code.csharp.controlflow.BasicBlocks
1113
private import semmle.code.csharp.controlflow.internal.Completion
1214
private import semmle.code.csharp.dataflow.Nullness
1315
private import semmle.code.csharp.frameworks.System
16+
private import semmle.code.cil.CallableReturns
1417

1518
/** An abstract value. */
1619
abstract class AbstractValue extends TAbstractValue {
@@ -637,6 +640,15 @@ module Internal {
637640
TMatchValue(CaseStmt cs, boolean b) { b = true or b = false } or
638641
TEmptyCollectionValue(boolean b) { b = true or b = false }
639642

643+
/** A callable that always returns a non-`null` value. */
644+
private class NonNullCallable extends DotNet::Callable {
645+
NonNullCallable() {
646+
exists(CIL::Method m | m.matchesHandle(this) | alwaysNotNullMethod(m) and not m.isVirtual())
647+
or
648+
this = any(SystemObjectClass c).getGetTypeMethod()
649+
}
650+
}
651+
640652
/** Holds if expression `e` is a non-`null` value. */
641653
predicate nonNullValue(Expr e) {
642654
e instanceof ObjectCreation
@@ -652,12 +664,10 @@ module Internal {
652664
e instanceof AddExpr and
653665
e.getType() instanceof StringType
654666
or
655-
e = any(MethodCall mc |
656-
mc.getTarget() = any(SystemObjectClass c).getGetTypeMethod() and
657-
not mc.isConditional()
658-
)
659-
or
660667
e.(DefaultValueExpr).getType().isValueType()
668+
or
669+
e.(Call).getTarget().getSourceDeclaration() instanceof NonNullCallable and
670+
not e.(QualifiableExpr).isConditional()
661671
}
662672

663673
/** Holds if expression `e2` is a non-`null` value whenever `e1` is. */
@@ -1288,9 +1298,7 @@ module Internal {
12881298
) {
12891299
isGuardedByNode1(guarded, g, sub, v) and
12901300
sub = g.getAChildExpr*() and
1291-
forall(Ssa::Definition def | def = sub.getAnSsaQualifier(_) |
1292-
isGuardedByNode2(guarded, def)
1293-
)
1301+
forall(Ssa::Definition def | def = sub.getAnSsaQualifier(_) | isGuardedByNode2(guarded, def))
12941302
}
12951303

12961304
/**

csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,7 @@ module DataFlow {
11881188
* nodes that may potentially be reached in flow from some source to some
11891189
* sink.
11901190
*/
1191-
module Pruning {
1191+
private module Pruning {
11921192
/**
11931193
* Holds if `node` is reachable from a source in the configuration `config`,
11941194
* ignoring call contexts.

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ class AlwaysNullExpr extends Expr {
6161
or
6262
this instanceof DefaultValueExpr and this.getType().isRefType()
6363
or
64+
this = any(Ssa::Definition def |
65+
forex(Ssa::Definition u | u = def.getAnUltimateDefinition() | nullDef(u))
66+
).getARead()
67+
or
6468
exists(Callable target |
6569
this.(Call).getTarget() = target and
6670
not target.(Virtualizable).isVirtual() and
@@ -69,6 +73,11 @@ class AlwaysNullExpr extends Expr {
6973
}
7074
}
7175

76+
/** Holds if SSA definition `def` is always `null`. */
77+
private predicate nullDef(Ssa::ExplicitDefinition def) {
78+
def.getADefinition().getSource() instanceof AlwaysNullExpr
79+
}
80+
7281
/** An expression that is never `null`. */
7382
class NonNullExpr extends Expr {
7483
NonNullExpr() {
@@ -78,7 +87,9 @@ class NonNullExpr extends Expr {
7887
or
7988
this instanceof G::NullGuardedExpr
8089
or
81-
exists(Ssa::Definition def | nonNullDef(def) | this = def.getARead())
90+
this = any(Ssa::Definition def |
91+
forex(Ssa::Definition u | u = def.getAnUltimateDefinition() | nonNullDef(u))
92+
).getARead()
8293
or
8394
exists(Callable target |
8495
this.(Call).getTarget() = target and
@@ -90,10 +101,10 @@ class NonNullExpr extends Expr {
90101
}
91102

92103
/** Holds if SSA definition `def` is never `null`. */
93-
private predicate nonNullDef(Ssa::Definition v) {
94-
v.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof NonNullExpr
104+
private predicate nonNullDef(Ssa::ExplicitDefinition def) {
105+
def.getADefinition().getSource() instanceof NonNullExpr
95106
or
96-
exists(AssignableDefinition ad | ad = v.(Ssa::ExplicitDefinition).getADefinition() |
107+
exists(AssignableDefinition ad | ad = def.getADefinition() |
97108
ad instanceof AssignableDefinitions::IsPatternDefinition
98109
or
99110
ad instanceof AssignableDefinitions::TypeCasePatternDefinition

csharp/ql/test/library-tests/cil/dataflow/DataFlow.ql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import csharp
22
import semmle.code.csharp.dataflow.DataFlow::DataFlow
3-
// import DataFlow::PathGraph
4-
3+
54
class FlowConfig extends Configuration {
65
FlowConfig() { this = "FlowConfig" }
76

csharp/ql/test/library-tests/cil/dataflow/Nullness.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
alwaysNull
22
| dataflow.cs:70:21:70:35 | default(...) |
3+
| dataflow.cs:74:21:74:34 | call to method NullFunction |
34
| dataflow.cs:74:39:74:52 | call to method IndirectNull |
45
| dataflow.cs:78:21:78:45 | call to method ReturnsNull |
56
| dataflow.cs:79:21:79:46 | call to method ReturnsNull2 |
67
| dataflow.cs:80:21:80:44 | access to property NullProperty |
8+
| dataflow.cs:89:31:89:44 | call to method NullFunction |
79
alwaysNotNull
810
| dataflow.cs:71:24:71:35 | default(...) |
911
| dataflow.cs:72:27:72:30 | this access |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// semmle-extractor-options: --cil

csharp/ql/test/query-tests/Nullness/Implications.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,8 @@
952952
| D.cs:212:18:212:18 | access to local variable n | null | D.cs:211:20:211:23 | null | null |
953953
| D.cs:212:18:212:26 | ... == ... | false | D.cs:212:18:212:18 | access to local variable n | non-null |
954954
| D.cs:212:18:212:26 | ... == ... | true | D.cs:212:18:212:18 | access to local variable n | null |
955+
| D.cs:212:18:212:45 | ... ? ... : ... | non-null | D.cs:212:18:212:26 | ... == ... | true |
956+
| D.cs:212:18:212:45 | ... ? ... : ... | non-null | D.cs:212:30:212:41 | object creation of type Object | non-null |
955957
| D.cs:212:18:212:45 | ... ? ... : ... | null | D.cs:212:18:212:26 | ... == ... | false |
956958
| D.cs:212:18:212:45 | ... ? ... : ... | null | D.cs:212:45:212:45 | access to local variable n | null |
957959
| D.cs:212:45:212:45 | access to local variable n | non-null | D.cs:211:20:211:23 | null | non-null |

0 commit comments

Comments
 (0)