Skip to content

Commit ad8ae35

Browse files
authored
Merge pull request github#1956 from hvitved/csharp/get-an-out-node
C#: Refactor `getAnOutNode()` predicate
2 parents fb20cab + b7595ed commit ad8ae35

File tree

4 files changed

+66
-73
lines changed

4 files changed

+66
-73
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ module Stages {
7272
or
7373
exists(any(DataFlow::Node n).toString())
7474
or
75-
exists(any(OutNode n).getCall())
75+
exists(any(OutNode n).getCall(_))
7676
or
7777
exists(CallContext cc)
7878
or

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

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -172,64 +172,16 @@ private module Cached {
172172
result = viableImplInCallContext(call, ctx) and
173173
reducedViableImplInReturn(result, call)
174174
}
175-
176-
/** A valid return type for a method that uses `yield return`. */
177-
private class YieldReturnType extends Type {
178-
YieldReturnType() {
179-
exists(Type t | t = this.getSourceDeclaration() |
180-
t instanceof SystemCollectionsIEnumerableInterface
181-
or
182-
t instanceof SystemCollectionsIEnumeratorInterface
183-
or
184-
t instanceof SystemCollectionsGenericIEnumerableTInterface
185-
or
186-
t instanceof SystemCollectionsGenericIEnumeratorInterface
187-
)
188-
}
189-
}
190-
191-
/**
192-
* Gets a node that can read the value returned from `call` with return kind
193-
* `kind`.
194-
*/
195-
cached
196-
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
197-
// normal `return`
198-
result = call.getNode() and
199-
kind instanceof NormalReturnKind and
200-
not call.getExpr().getType() instanceof VoidType
201-
or
202-
// `yield return`
203-
result = call.getNode() and
204-
kind instanceof YieldReturnKind and
205-
call.getExpr().getType() instanceof YieldReturnType
206-
or
207-
// `out`/`ref` parameter
208-
exists(Parameter p, AssignableDefinitions::OutRefDefinition def |
209-
p.getSourceDeclaration().getPosition() = kind.(OutRefReturnKind).getPosition()
210-
|
211-
def = result.(SsaDefinitionNode).getDefinition().(Ssa::ExplicitDefinition).getADefinition() and
212-
def.getTargetAccess() = call.getExpr().(Call).getArgumentForParameter(p)
213-
)
214-
or
215-
// implicit captured variable return
216-
exists(Ssa::ExplicitDefinition def, Ssa::ImplicitCallDefinition cdef, LocalScopeVariable v |
217-
kind.(ImplicitCapturedReturnKind).getVariable() = v and
218-
def.isCapturedVariableDefinitionFlowOut(cdef, _) and
219-
cdef = result.(SsaDefinitionNode).getDefinition() and
220-
v = def.getSourceVariable().getAssignable()
221-
|
222-
call.getControlFlowNode() = cdef.getControlFlowNode()
223-
or
224-
exists(DataFlowCall outer | call.(ImplicitDelegateDataFlowCall).isArgumentOf(outer, _) |
225-
outer.getControlFlowNode() = cdef.getControlFlowNode()
226-
)
227-
)
228-
}
229175
}
230176

231177
import Cached
232178

179+
/**
180+
* Gets a node that can read the value returned from `call` with return kind
181+
* `kind`.
182+
*/
183+
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) }
184+
233185
predicate viableCallable = viableImpl/1;
234186

235187
/**
@@ -394,6 +346,9 @@ class ImplicitDelegateDataFlowCall extends DelegateDataFlowCall, TImplicitDelega
394346
/** Gets the number of parameters of the supplied delegate. */
395347
int getNumberOfDelegateParameters() { result = arg.getDelegateType().getNumberOfParameters() }
396348

349+
/** Gets the return type of the supplied delegate. */
350+
Type getDelegateReturnType() { result = arg.getDelegateType().getReturnType() }
351+
397352
override DotNet::Callable getARuntimeTarget(CallContext::CallContext cc) {
398353
result = cfn.getElement().(DelegateArgumentToLibraryCallable).getARuntimeTarget(cc)
399354
}

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

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -944,17 +944,35 @@ import ReturnNodes
944944

945945
/** A data flow node that represents the output of a call. */
946946
abstract class OutNode extends Node {
947-
/** Gets the underlying call. */
947+
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
948948
cached
949-
abstract DataFlowCall getCall();
949+
abstract DataFlowCall getCall(ReturnKind kind);
950950
}
951951

952952
private module OutNodes {
953+
private import semmle.code.csharp.frameworks.system.Collections
954+
private import semmle.code.csharp.frameworks.system.collections.Generic
955+
953956
private DataFlowCall csharpCall(Expr e, ControlFlow::Node cfn) {
954957
e = any(DispatchCall dc | result = TNonDelegateCall(cfn, dc)).getCall() or
955958
result = TExplicitDelegateCall(cfn, e)
956959
}
957960

961+
/** A valid return type for a method that uses `yield return`. */
962+
private class YieldReturnType extends Type {
963+
YieldReturnType() {
964+
exists(Type t | t = this.getSourceDeclaration() |
965+
t instanceof SystemCollectionsIEnumerableInterface
966+
or
967+
t instanceof SystemCollectionsIEnumeratorInterface
968+
or
969+
t instanceof SystemCollectionsGenericIEnumerableTInterface
970+
or
971+
t instanceof SystemCollectionsGenericIEnumeratorInterface
972+
)
973+
}
974+
}
975+
958976
/**
959977
* A data flow node that reads a value returned directly by a callable,
960978
* either via a C# call or a CIL call.
@@ -969,8 +987,16 @@ private module OutNodes {
969987
)
970988
}
971989

972-
override DataFlowCall getCall() {
973-
Stages::DataFlowStage::forceCachingInSameStage() and result = call
990+
override DataFlowCall getCall(ReturnKind kind) {
991+
Stages::DataFlowStage::forceCachingInSameStage() and
992+
result = call and
993+
(
994+
kind instanceof NormalReturnKind and
995+
not call.getExpr().getType() instanceof VoidType
996+
or
997+
kind instanceof YieldReturnKind and
998+
call.getExpr().getType() instanceof YieldReturnType
999+
)
9741000
}
9751001
}
9761002

@@ -995,21 +1021,30 @@ private module OutNodes {
9951021
)
9961022
}
9971023

998-
override DataFlowCall getCall() { result = call }
1024+
override DataFlowCall getCall(ReturnKind kind) {
1025+
result = call and
1026+
kind.(ImplicitCapturedReturnKind).getVariable() = this
1027+
.getDefinition()
1028+
.getSourceVariable()
1029+
.getAssignable()
1030+
}
9991031
}
10001032

10011033
/**
10021034
* A data flow node that reads a value returned by a callable using an
10031035
* `out` or `ref` parameter.
10041036
*/
10051037
class ParamOutNode extends OutNode, SsaDefinitionNode {
1006-
ParamOutNode() {
1007-
this.getDefinition().(Ssa::ExplicitDefinition).getADefinition() instanceof
1008-
AssignableDefinitions::OutRefDefinition
1009-
}
1038+
private AssignableDefinitions::OutRefDefinition outRefDef;
10101039

1011-
override DataFlowCall getCall() {
1012-
result = csharpCall(_, this.getDefinition().getControlFlowNode())
1040+
ParamOutNode() { outRefDef = this.getDefinition().(Ssa::ExplicitDefinition).getADefinition() }
1041+
1042+
override DataFlowCall getCall(ReturnKind kind) {
1043+
result = csharpCall(_, this.getDefinition().getControlFlowNode()) and
1044+
exists(Parameter p |
1045+
p.getSourceDeclaration().getPosition() = kind.(OutRefReturnKind).getPosition() and
1046+
outRefDef.getTargetAccess() = result.getExpr().(Call).getArgumentForParameter(p)
1047+
)
10131048
}
10141049
}
10151050

@@ -1036,7 +1071,16 @@ private module OutNodes {
10361071

10371072
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
10381073

1039-
override ImplicitDelegateDataFlowCall getCall() { result.getNode() = this }
1074+
override ImplicitDelegateDataFlowCall getCall(ReturnKind kind) {
1075+
result.getNode() = this and
1076+
(
1077+
kind instanceof NormalReturnKind and
1078+
not result.getDelegateReturnType() instanceof VoidType
1079+
or
1080+
kind instanceof YieldReturnKind and
1081+
result.getDelegateReturnType() instanceof YieldReturnType
1082+
)
1083+
}
10401084

10411085
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
10421086

csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,16 @@
44
| Capture.cs:33:30:33:39 | [implicit call] access to local variable captureIn3 | return | Capture.cs:33:30:33:39 | [output] access to local variable captureIn3 |
55
| Capture.cs:71:9:71:21 | call to local function CaptureOut1 | captured sink30 | Capture.cs:71:9:71:21 | SSA call def(sink30) |
66
| Capture.cs:83:9:83:21 | [transitive] call to local function CaptureOut2 | captured sink31 | Capture.cs:83:9:83:21 | SSA call def(sink31) |
7-
| Capture.cs:83:9:83:21 | call to local function CaptureOut2 | captured sink31 | Capture.cs:83:9:83:21 | SSA call def(sink31) |
87
| Capture.cs:92:9:92:41 | call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) |
98
| Capture.cs:92:9:92:41 | call to method Select | return | Capture.cs:92:9:92:41 | call to method Select |
109
| Capture.cs:92:9:92:41 | call to method Select | yield return | Capture.cs:92:9:92:41 | call to method Select |
1110
| Capture.cs:92:9:92:51 | call to method ToArray | return | Capture.cs:92:9:92:51 | call to method ToArray |
1211
| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) |
1312
| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | return | Capture.cs:92:30:92:40 | [output] access to local variable captureOut3 |
1413
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) |
15-
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) |
16-
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) |
1714
| Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) |
18-
| Capture.cs:121:9:121:35 | call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) |
19-
| Capture.cs:121:9:121:35 | call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) |
2015
| Capture.cs:132:9:132:25 | call to local function CaptureThrough1 | captured sink33 | Capture.cs:132:9:132:25 | SSA call def(sink33) |
2116
| Capture.cs:144:9:144:25 | [transitive] call to local function CaptureThrough2 | captured sink34 | Capture.cs:144:9:144:25 | SSA call def(sink34) |
22-
| Capture.cs:144:9:144:25 | call to local function CaptureThrough2 | captured sink34 | Capture.cs:144:9:144:25 | SSA call def(sink34) |
2317
| Capture.cs:153:9:153:45 | call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) |
2418
| Capture.cs:153:9:153:45 | call to method Select | return | Capture.cs:153:9:153:45 | call to method Select |
2519
| Capture.cs:153:9:153:45 | call to method Select | yield return | Capture.cs:153:9:153:45 | call to method Select |

0 commit comments

Comments
 (0)