Skip to content

Commit dee38d6

Browse files
committed
C#: Add preservesValue to NonLocalJumpNode.getAJumpSuccessor. Allow DataFlow::Configuration::isAdditionalFlowStep to jump between callables.
1 parent f3c3526 commit dee38d6

10 files changed

Lines changed: 51 additions & 37 deletions

File tree

change-notes/1.20/analysis-csharp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
* Support has been added for EntityFrameworkCore, including
3232
- Stored data flow sources
3333
- Sinks for SQL expressions
34-
- Dataflow through fields that are mapped to the database.
34+
- Data flow through fields that are mapped to the database.
3535

3636
## Changes to the autobuilder

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ module DataFlow {
124124
*/
125125
abstract class NonLocalJumpNode extends Node {
126126
/** Gets a successor node that is potentially in another callable. */
127-
abstract Node getAJumpSuccessor();
127+
abstract Node getAJumpSuccessor(boolean preservesValue);
128128
}
129129

130130
/**
@@ -1072,8 +1072,10 @@ module DataFlow {
10721072
*/
10731073
bindingset[config]
10741074
predicate localFlowStep(Node pred, Node succ, Configuration config) {
1075-
localFlowStepNoConfig(pred, succ) or
1076-
config.isAdditionalFlowStep(pred, succ)
1075+
localFlowStepNoConfig(pred, succ)
1076+
or
1077+
config.isAdditionalFlowStep(pred, succ) and
1078+
pred.getEnclosingCallable() = succ.getEnclosingCallable()
10771079
}
10781080

10791081
/**
@@ -1084,7 +1086,7 @@ module DataFlow {
10841086
Pruning::nodeCand(node, config) and
10851087
(
10861088
config.isSource(node) or
1087-
jumpStep(_, node) or
1089+
additionalJumpStep(_, node, config) or
10881090
node instanceof ParameterNode or
10891091
node instanceof OutNode
10901092
)
@@ -1097,7 +1099,7 @@ module DataFlow {
10971099
predicate localFlowExit(Node node, Configuration config) {
10981100
Pruning::nodeCand(node, config) and
10991101
(
1100-
jumpStep(node, _) or
1102+
additionalJumpStep(node, _, config) or
11011103
node instanceof ArgumentNode or
11021104
node instanceof ReturnNode or
11031105
config.isSink(node)
@@ -1138,6 +1140,17 @@ module DataFlow {
11381140
}
11391141
}
11401142

1143+
1144+
/**
1145+
* Holds if the additional step from `node1` to `node2` jumps between callables.
1146+
*/
1147+
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
1148+
config.isAdditionalFlowStep(node1, node2) and
1149+
node1.getEnclosingCallable() != node2.getEnclosingCallable()
1150+
or
1151+
jumpStep(node1, node2)
1152+
}
1153+
11411154
/**
11421155
* Provides predicates for pruning the data flow graph, by only including
11431156
* nodes that may potentially be reached in flow from some source to some
@@ -1155,7 +1168,7 @@ module DataFlow {
11551168
or
11561169
exists(Node mid | nodeCandFwd1(mid, config) | LocalFlow::localFlowStep(mid, node, config))
11571170
or
1158-
exists(Node mid | nodeCandFwd1(mid, config) | jumpStep(mid, node))
1171+
exists(Node mid | nodeCandFwd1(mid, config) | additionalJumpStep(mid, node, config))
11591172
or
11601173
exists(ArgumentNode arg | nodeCandFwd1(arg, config) |
11611174
flowIntoCallableStep(_, arg, node, _, config)
@@ -1178,7 +1191,7 @@ module DataFlow {
11781191
or
11791192
exists(Node mid | nodeCand1(mid, config) | LocalFlow::localFlowStep(node, mid, config))
11801193
or
1181-
exists(Node mid | nodeCand1(mid, config) | jumpStep(node, mid))
1194+
exists(Node mid | nodeCand1(mid, config) | additionalJumpStep(node, mid, config))
11821195
or
11831196
exists(ParameterNode p | nodeCand1(p, config) |
11841197
flowIntoCallableStep(_, node, p, _, config)
@@ -1261,7 +1274,7 @@ module DataFlow {
12611274
or
12621275
nodeCand1(node, config) and
12631276
exists(Node mid | nodeCandFwd2(mid, _, config) |
1264-
jumpStep(mid, node) and
1277+
additionalJumpStep(mid, node, config) and
12651278
fromArg = false
12661279
)
12671280
or
@@ -1317,7 +1330,7 @@ module DataFlow {
13171330
or
13181331
nodeCandFwd2(node, _, config) and
13191332
exists(Node mid | nodeCand2(mid, _, config) |
1320-
jumpStep(node, mid) and
1333+
additionalJumpStep(node, mid, config) and
13211334
isReturned = false
13221335
)
13231336
or
@@ -1376,7 +1389,7 @@ module DataFlow {
13761389
*/
13771390
cached
13781391
predicate jumpStep(ExprNode pred, ExprNode succ) {
1379-
pred.(NonLocalJumpNode).getAJumpSuccessor() = succ
1392+
pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ
13801393
}
13811394

13821395
/** A dataflow node that has field-like dataflow. */
@@ -1395,7 +1408,9 @@ module DataFlow {
13951408
hasNonlocalValue(flr)
13961409
}
13971410

1398-
override ExprNode getAJumpSuccessor() { result = succ }
1411+
override ExprNode getAJumpSuccessor(boolean preservesValue) {
1412+
result = succ and preservesValue = true
1413+
}
13991414
}
14001415

14011416
/**
@@ -1681,7 +1696,7 @@ module DataFlow {
16811696
ctx = mid.getContext() and
16821697
LocalFlow::localFlowBigStep(mid.getNode(), node, mid.getConfiguration())
16831698
or
1684-
jumpStep(mid.getNode(), node) and
1699+
additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
16851700
ctx instanceof NoContext
16861701
or
16871702
flowIntoCallable(mid, node, ctx)

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,13 @@ module TaintTracking {
8080
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
8181

8282
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
83-
isAdditionalTaintStep(pred, succ) or
84-
localAdditionalTaintStep(pred, succ) or
83+
isAdditionalTaintStep(pred, succ)
84+
or
85+
localAdditionalTaintStep(pred, succ)
86+
or
8587
DataFlow::Internal::flowThroughCallableLibraryOutRef(_, pred, succ, false)
88+
or
89+
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
8690
}
8791

8892
final override predicate isAdditionalFlowStepIntoCall(

csharp/ql/src/semmle/code/csharp/frameworks/EntityFramework.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,9 @@ module EntityFramework {
185185

186186
MappedPropertyJumpNode() { this.asExpr() = property.getAnAssignedValue() }
187187

188-
override DataFlow::Node getAJumpSuccessor() {
189-
result.asExpr().(PropertyRead).getTarget() = property
188+
override DataFlow::Node getAJumpSuccessor(boolean preservesValue) {
189+
result.asExpr().(PropertyRead).getTarget() = property and
190+
preservesValue = false
190191
}
191192
}
192193
}

csharp/ql/test/library-tests/frameworks/EntityFramework/Dataflow.ql

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import csharp
22
import semmle.code.csharp.dataflow.TaintTracking
33

4-
class MyConfiguration extends TaintTracking::Configuration
5-
{
4+
class MyConfiguration extends TaintTracking::Configuration {
65
MyConfiguration() { this = "EntityFramework dataflow" }
7-
8-
override predicate isSource(DataFlow::Node node) {
9-
node.asExpr().getValue() = "tainted"
10-
}
11-
6+
7+
override predicate isSource(DataFlow::Node node) { node.asExpr().getValue() = "tainted" }
8+
129
override predicate isSink(DataFlow::Node node) {
1310
node.asExpr() = any(MethodCall c | c.getTarget().hasName("Sink")).getAnArgument()
1411
}

csharp/ql/test/library-tests/frameworks/EntityFramework/EntityFramework.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@ class MyContext : DbContext
2323
void FlowSources()
2424
{
2525
var p = new Person();
26-
var id = p.Id;
27-
var name = p.Name;
28-
var age = p.Age;
26+
var id = p.Id; // Remote flow source
27+
var name = p.Name; // Remote flow source
28+
var age = p.Age; // Not a remote flow source
2929
}
3030

3131
DbCommand command;
3232

3333
async void SqlSinks()
3434
{
3535
// System.Data.Common.DbCommand.set_CommandText
36-
command.CommandText = "";
37-
36+
command.CommandText = ""; // SqlExpr
37+
3838
// System.Data.SqlClient.SqlCommand.SqlCommand
39-
new System.Data.SqlClient.SqlCommand("");
40-
41-
this.Database.ExecuteSqlCommand("");
42-
await this.Database.ExecuteSqlCommandAsync("");
39+
new System.Data.SqlClient.SqlCommand(""); // SqlExpr
40+
41+
this.Database.ExecuteSqlCommand(""); // SqlExpr
42+
await this.Database.ExecuteSqlCommandAsync(""); // SqlExpr
4343
}
4444

4545
void TestDataFlow()
@@ -59,7 +59,6 @@ void TestDataFlow()
5959
void Sink(object @object)
6060
{
6161
}
62-
6362
}
6463

6564
}

csharp/ql/test/library-tests/frameworks/EntityFramework/MappedProperties.ql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,3 @@ import semmle.code.csharp.frameworks.EntityFramework
33

44
from EntityFramework::MappedProperty property
55
select property
6-

csharp/ql/test/library-tests/frameworks/EntityFramework/SqlExprs.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ import csharp
22
import semmle.code.csharp.frameworks.Sql
33

44
from SqlExpr expr
5-
select expr
5+
select expr

csharp/ql/test/library-tests/frameworks/EntityFramework/FlowSources.expected renamed to csharp/ql/test/library-tests/frameworks/EntityFramework/StoredFlowSources.expected

File renamed without changes.

csharp/ql/test/library-tests/frameworks/EntityFramework/FlowSources.ql renamed to csharp/ql/test/library-tests/frameworks/EntityFramework/StoredFlowSources.ql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import csharp
2-
32
import semmle.code.csharp.security.dataflow.flowsources.Stored
43

54
from StoredFlowSource source

0 commit comments

Comments
 (0)