Skip to content

Commit bfe4a4b

Browse files
committed
C#: Additional tracking of lambdas through fields and properties
1 parent 817d04c commit bfe4a4b

File tree

4 files changed

+105
-23
lines changed

4 files changed

+105
-23
lines changed

csharp/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ private import codeql.dataflow.internal.DataFlowImplConsistency
77
private module Input implements InputSig<CsharpDataFlow> {
88
private import CsharpDataFlow
99

10-
predicate uniqueEnclosingCallableExclude(Node n) {
11-
// TODO: Remove once static initializers are folded into the
12-
// static constructors
13-
exists(ControlFlow::Node cfn |
14-
cfn.getAstNode() = any(FieldOrProperty f | f.isStatic()).getAChild+() and
15-
cfn = n.getControlFlowNode()
16-
)
17-
}
18-
1910
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
2011
// TODO: Remove once static initializers are folded into the
2112
// static constructors
@@ -30,6 +21,8 @@ private module Input implements InputSig<CsharpDataFlow> {
3021
n instanceof ParameterNode
3122
or
3223
missingLocationExclude(n)
24+
or
25+
n instanceof FlowInsensitiveFieldNode
3326
}
3427

3528
predicate missingLocationExclude(Node n) {

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ private module Cached {
9898
cached
9999
newtype TDataFlowCallable =
100100
TDotNetCallable(DotNet::Callable c) { c.isUnboundDeclaration() } or
101-
TSummarizedCallable(DataFlowSummarizedCallable sc)
101+
TSummarizedCallable(DataFlowSummarizedCallable sc) or
102+
TFieldOrProperty(FieldOrProperty f)
102103

103104
cached
104105
newtype TDataFlowCall =
@@ -247,22 +248,33 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
247248

248249
/** A callable used for data flow. */
249250
class DataFlowCallable extends TDataFlowCallable {
250-
/** Get the underlying source code callable, if any. */
251+
/** Gets the underlying source code callable, if any. */
251252
DotNet::Callable asCallable() { this = TDotNetCallable(result) }
252253

253-
/** Get the underlying summarized callable, if any. */
254+
/** Gets the underlying summarized callable, if any. */
254255
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
255256

256-
/** Get the underlying callable. */
257+
/** Gets the underlying field or property, if any. */
258+
FieldOrProperty asFieldOrProperty() { this = TFieldOrProperty(result) }
259+
260+
/** Gets the underlying callable. */
257261
DotNet::Callable getUnderlyingCallable() {
258262
result = this.asCallable() or result = this.asSummarizedCallable()
259263
}
260264

261265
/** Gets a textual representation of this dataflow callable. */
262-
string toString() { result = this.getUnderlyingCallable().toString() }
266+
string toString() {
267+
result = this.getUnderlyingCallable().toString()
268+
or
269+
result = this.asFieldOrProperty().toString()
270+
}
263271

264272
/** Get the location of this dataflow callable. */
265-
Location getLocation() { result = this.getUnderlyingCallable().getLocation() }
273+
Location getLocation() {
274+
result = this.getUnderlyingCallable().getLocation()
275+
or
276+
result = this.asFieldOrProperty().getLocation()
277+
}
266278
}
267279

268280
/** A call relevant for data flow. */

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,26 @@ abstract class NodeImpl extends Node {
6969
abstract string toStringImpl();
7070
}
7171

72+
// TODO: Remove once static initializers are folded into the
73+
// static constructors
74+
private DataFlowCallable getEnclosingStaticFieldOrProperty(Expr e) {
75+
result.asFieldOrProperty() =
76+
any(FieldOrProperty f |
77+
f.isStatic() and
78+
e = f.getAChild+() and
79+
not exists(e.getEnclosingCallable())
80+
)
81+
}
82+
7283
private class ExprNodeImpl extends ExprNode, NodeImpl {
7384
override DataFlowCallable getEnclosingCallableImpl() {
7485
result.asCallable() =
7586
[
7687
this.getExpr().(CIL::Expr).getEnclosingCallable().(DotNet::Callable),
7788
this.getControlFlowNodeImpl().getEnclosingCallable()
7889
]
90+
or
91+
result = getEnclosingStaticFieldOrProperty(this.asExpr())
7992
}
8093

8194
override DotNet::Type getTypeImpl() {
@@ -909,7 +922,8 @@ private module Cached {
909922
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
910923
TParamsArgumentNode(ControlFlow::Node callCfn) {
911924
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
912-
}
925+
} or
926+
TFlowInsensitiveFieldNode(FieldOrProperty f) { f.isFieldLike() }
913927

914928
/**
915929
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -1019,6 +1033,8 @@ predicate nodeIsHidden(Node n) {
10191033
n instanceof ParamsArgumentNode
10201034
or
10211035
n.asExpr() = any(WithExpr we).getInitializer()
1036+
or
1037+
n instanceof FlowInsensitiveFieldNode
10221038
}
10231039

10241040
/** A CIL SSA definition, viewed as a node in a data flow graph. */
@@ -1344,6 +1360,8 @@ private module ArgumentNodes {
13441360

13451361
override DataFlowCallable getEnclosingCallableImpl() {
13461362
result.asCallable() = cfn.getEnclosingCallable()
1363+
or
1364+
result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
13471365
}
13481366

13491367
override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
@@ -1383,6 +1401,8 @@ private module ArgumentNodes {
13831401

13841402
override DataFlowCallable getEnclosingCallableImpl() {
13851403
result.asCallable() = callCfn.getEnclosingCallable()
1404+
or
1405+
result = getEnclosingStaticFieldOrProperty(callCfn.getAstNode())
13861406
}
13871407

13881408
override Type getTypeImpl() { result = this.getParameter().getType() }
@@ -1782,6 +1802,30 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead
17821802
}
17831803
}
17841804

1805+
/**
1806+
* A data flow node used for control-flow insensitive flow through fields
1807+
* and properties.
1808+
*
1809+
* In global data flow this is used to model flow through static fields and
1810+
* properties, while for lambda flow we additionally use it to track assignments
1811+
* in constructors to uses within the same class.
1812+
*/
1813+
class FlowInsensitiveFieldNode extends NodeImpl, TFlowInsensitiveFieldNode {
1814+
private FieldOrProperty f;
1815+
1816+
FlowInsensitiveFieldNode() { this = TFlowInsensitiveFieldNode(f) }
1817+
1818+
override DataFlowCallable getEnclosingCallableImpl() { result.asFieldOrProperty() = f }
1819+
1820+
override Type getTypeImpl() { result = f.getType() }
1821+
1822+
override ControlFlow::Node getControlFlowNodeImpl() { none() }
1823+
1824+
override Location getLocationImpl() { result = f.getLocation() }
1825+
1826+
override string toStringImpl() { result = "[flow-insensitive] " + f }
1827+
}
1828+
17851829
/**
17861830
* Holds if `pred` can flow to `succ`, by jumping from one callable to
17871831
* another. Additional steps specified by the configuration are *not*
@@ -1790,13 +1834,16 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead
17901834
predicate jumpStep(Node pred, Node succ) {
17911835
pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ
17921836
or
1793-
exists(FieldOrProperty fl, FieldOrPropertyRead flr |
1794-
fl.isStatic() and
1795-
fl.isFieldLike() and
1796-
fl.getAnAssignedValue() = pred.asExpr() and
1797-
fl.getAnAccess() = flr and
1798-
flr = succ.asExpr() and
1799-
flr.hasNonlocalValue()
1837+
exists(FieldOrProperty f | f.isStatic() |
1838+
f.getAnAssignedValue() = pred.asExpr() and
1839+
succ = TFlowInsensitiveFieldNode(f)
1840+
or
1841+
exists(FieldOrPropertyRead fr |
1842+
pred = TFlowInsensitiveFieldNode(f) and
1843+
f.getAnAccess() = fr and
1844+
fr = succ.asExpr() and
1845+
fr.hasNonlocalValue()
1846+
)
18001847
)
18011848
or
18021849
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
@@ -2248,6 +2295,8 @@ module PostUpdateNodes {
22482295

22492296
override DataFlowCallable getEnclosingCallableImpl() {
22502297
result.asCallable() = cfn.getEnclosingCallable()
2298+
or
2299+
result = getEnclosingStaticFieldOrProperty(oc)
22512300
}
22522301

22532302
override DotNet::Type getTypeImpl() { result = oc.getType() }
@@ -2279,6 +2328,8 @@ module PostUpdateNodes {
22792328

22802329
override DataFlowCallable getEnclosingCallableImpl() {
22812330
result.asCallable() = cfn.getEnclosingCallable()
2331+
or
2332+
result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
22822333
}
22832334

22842335
override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
@@ -2427,6 +2478,24 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
24272478
nodeTo.asExpr().(EventRead).getTarget() = aee.getTarget() and
24282479
preservesValue = false
24292480
)
2481+
or
2482+
preservesValue = true and
2483+
exists(FieldOrProperty f, FieldOrPropertyAccess fa |
2484+
fa = f.getAnAccess() and
2485+
fa.targetIsLocalInstance()
2486+
|
2487+
exists(AssignableDefinition def |
2488+
def.getTargetAccess() = fa and
2489+
nodeFrom.asExpr() = def.getSource() and
2490+
nodeTo = TFlowInsensitiveFieldNode(f) and
2491+
nodeFrom.getEnclosingCallable() instanceof Constructor
2492+
)
2493+
or
2494+
nodeFrom = TFlowInsensitiveFieldNode(f) and
2495+
f.getAnAccess() = fa and
2496+
fa = nodeTo.asExpr() and
2497+
fa.(FieldOrPropertyRead).hasNonlocalValue()
2498+
)
24302499
}
24312500

24322501
/**

csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ delegateCall
2424
| DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
2525
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:25 | (...) => ... |
2626
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:37 | (...) => ... |
27+
| DelegateFlow.cs:153:9:153:21 | delegate call | DelegateFlow.cs:149:13:149:20 | (...) => ... |
28+
| DelegateFlow.cs:154:9:154:21 | delegate call | DelegateFlow.cs:150:13:150:20 | (...) => ... |
29+
| DelegateFlow.cs:155:9:155:16 | delegate call | DelegateFlow.cs:149:13:149:20 | (...) => ... |
30+
| DelegateFlow.cs:156:9:156:16 | delegate call | DelegateFlow.cs:150:13:150:20 | (...) => ... |
2731
viableLambda
2832
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:9:16:20 | call to method M2 | DelegateFlow.cs:16:12:16:19 | (...) => ... |
2933
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:17:9:17:14 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
@@ -51,5 +55,9 @@ viableLambda
5155
| DelegateFlow.cs:125:9:125:25 | function pointer call | file://:0:0:0:0 | (none) | DelegateFlow.cs:7:17:7:18 | M2 |
5256
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:41 | call to method M19 | DelegateFlow.cs:135:29:135:37 | (...) => ... |
5357
| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:25 | (...) => ... |
58+
| DelegateFlow.cs:153:9:153:21 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:149:13:149:20 | (...) => ... |
59+
| DelegateFlow.cs:154:9:154:21 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:150:13:150:20 | (...) => ... |
60+
| DelegateFlow.cs:155:9:155:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:149:13:149:20 | (...) => ... |
61+
| DelegateFlow.cs:156:9:156:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:150:13:150:20 | (...) => ... |
5462
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:105:9:105:24 | object creation of type Lazy<Int32> | DelegateFlow.cs:104:23:104:30 | (...) => ... |
5563
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:107:9:107:24 | object creation of type Lazy<Int32> | DelegateFlow.cs:106:13:106:20 | (...) => ... |

0 commit comments

Comments
 (0)