Merge pull request #5311 from hvitved/dataflow/lambda

Data flow: Move C# lambda flow logic into shared library
This commit is contained in:
Tom Hvitved
2021-03-19 11:44:15 +01:00
committed by GitHub
26 changed files with 1810 additions and 210 deletions

View File

@@ -1,4 +1,6 @@
/**
* DEPRECATED.
*
* Provides classes for data flow call contexts.
*/
@@ -15,11 +17,13 @@ private newtype TCallContext =
TArgFunctionPointerCallContext(FunctionPointerCall fptrc, int i) { exists(fptrc.getArgument(i)) }
/**
* DEPRECATED.
*
* A call context.
*
* A call context records the origin of data flow into callables.
*/
class CallContext extends TCallContext {
deprecated class CallContext extends TCallContext {
/** Gets a textual representation of this call context. */
string toString() { none() }
@@ -27,18 +31,20 @@ class CallContext extends TCallContext {
Location getLocation() { none() }
}
/** An empty call context. */
class EmptyCallContext extends CallContext, TEmptyCallContext {
/** DEPRECATED. An empty call context. */
deprecated class EmptyCallContext extends CallContext, TEmptyCallContext {
override string toString() { result = "<empty>" }
override EmptyLocation getLocation() { any() }
}
/**
* DEPRECATED.
*
* An argument call context, that is a call argument through which data flows
* into a callable.
*/
abstract class ArgumentCallContext extends CallContext {
abstract deprecated class ArgumentCallContext extends CallContext {
/**
* Holds if this call context represents the argument at position `i` of the
* call expression `call`.
@@ -46,8 +52,9 @@ abstract class ArgumentCallContext extends CallContext {
abstract predicate isArgument(Expr call, int i);
}
/** An argument of a non-delegate call. */
class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDelegateCallContext {
/** DEPRECATED. An argument of a non-delegate call. */
deprecated class NonDelegateCallArgumentCallContext extends ArgumentCallContext,
TArgNonDelegateCallContext {
Expr arg;
NonDelegateCallArgumentCallContext() { this = TArgNonDelegateCallContext(arg) }
@@ -61,8 +68,8 @@ class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDel
override Location getLocation() { result = arg.getLocation() }
}
/** An argument of a delegate or function pointer call. */
class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
/** DEPRECATED. An argument of a delegate or function pointer call. */
deprecated class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
DelegateLikeCall dc;
int arg;
@@ -80,10 +87,10 @@ class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
}
/** An argument of a delegate call. */
class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
/** DEPRECATED. An argument of a delegate call. */
deprecated class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgDelegateCallContext { }
/** An argument of a function pointer call. */
class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
/** DEPRECATED. An argument of a function pointer call. */
deprecated class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
TArgFunctionPointerCallContext { }

View File

@@ -1,8 +1,8 @@
private import csharp
private import cil
private import dotnet
private import DataFlowPublic
private import DataFlowPrivate
private import DelegateDataFlow
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dispatch.Dispatch
@@ -131,45 +131,24 @@ private module Cached {
import Cached
private module DispatchImpl {
private import CallContext
/**
* Gets a viable run-time target for the delegate call `call`, requiring
* call context `cc`.
*/
private DataFlowCallable viableDelegateCallable(DataFlowCall call, CallContext cc) {
result = call.(DelegateDataFlowCall).getARuntimeTarget(cc)
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the
* call is a delegate call, or if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, Callable c) {
c = call.getEnclosingCallable() and
(
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
not cc instanceof EmptyCallContext
)
or
call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext()
)
call.getDispatchCall().mayBenefitFromCallContext()
}
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) |
cc.isArgument(ctx.getExpr(), _)
)
or
DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) {
result =
call.(NonDelegateDataFlowCall)
.getDispatchCall()
call.getDispatchCall()
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
.getUnboundDeclaration()
}
@@ -301,12 +280,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
}
/** A delegate call relevant for data flow. */
abstract class DelegateDataFlowCall extends DataFlowCall {
/** Gets a viable run-time target of this call requiring call context `cc`. */
abstract DataFlowCallable getARuntimeTarget(CallContext::CallContext cc);
override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) }
}
abstract class DelegateDataFlowCall extends DataFlowCall { }
/** An explicit delegate or function pointer call relevant for data flow. */
class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateLikeCall {
@@ -315,8 +289,11 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
ExplicitDelegateLikeDataFlowCall() { this = TExplicitDelegateLikeCall(cfn, dc) }
override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
result = getCallableForDataFlow(dc.getARuntimeTarget(cc))
/** Gets the underlying call. */
DelegateLikeCall getCall() { result = dc }
override DataFlowCallable getARuntimeTarget() {
none() // handled by the shared library
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
@@ -389,11 +366,11 @@ class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall {
SummaryDelegateCall() { this = TSummaryDelegateCall(c, pos) }
override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
exists(SummaryDelegateParameterSink p |
p.isParameterOf(c, pos) and
result = p.getARuntimeTarget(cc)
)
/** Gets the parameter node that this delegate call targets. */
ParameterNode getParameterNode() { result.isParameterOf(c, pos) }
override DataFlowCallable getARuntimeTarget() {
none() // handled by the shared library
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }

View File

@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
/**
* Provides a simple data-flow analysis for resolving lambda calls. The analysis
* currently excludes read-steps, store-steps, and flow-through.
*
* The analysis uses non-linear recursion: When computing a flow path in or out
* of a call, we use the results of the analysis recursively to resolve lamba
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
*/
private module LambdaFlow {
private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
p.isParameterOf(viableCallable(call), i)
}
private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
p.isParameterOf(viableCallableLambda(call, _), i)
}
private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
exists(int i |
viableParamNonLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
exists(int i |
viableParamLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
private newtype TReturnPositionSimple =
TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
exists(ReturnNode ret |
c = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
pragma[noinline]
private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
}
pragma[nomagic]
private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
result = TReturnPositionSimple0(viableCallable(call), kind)
}
pragma[nomagic]
private TReturnPositionSimple viableReturnPosLambda(
DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
) {
result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
}
private predicate viableReturnPosOutNonLambda(
DataFlowCall call, TReturnPositionSimple pos, OutNode out
) {
exists(ReturnKind kind |
pos = viableReturnPosNonLambda(call, kind) and
out = getAnOutNode(call, kind)
)
}
private predicate viableReturnPosOutLambda(
DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
) {
exists(ReturnKind kind |
pos = viableReturnPosLambda(call, lastCall, kind) and
out = getAnOutNode(call, kind)
)
}
/**
* Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
* the lambda call `lambdaCall`.
*
* The parameter `toReturn` indicates whether the path from `node` to
* `lambdaCall` goes through a return, and `toJump` whether the path goes
* through a jump step.
*
* The call context `lastCall` records the last call on the path from `node`
* to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
* callable of `lambdaCall`.
*/
pragma[nomagic]
predicate revLambdaFlow(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
boolean toJump, DataFlowCallOption lastCall
) {
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
then compatibleTypes(t, getNodeType(node))
else any()
}
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
boolean toJump, DataFlowCallOption lastCall
) {
lambdaCall(lambdaCall, kind, node) and
t = getNodeType(node) and
toReturn = false and
toJump = false and
lastCall = TDataFlowCallNone()
or
// local flow
exists(Node mid, DataFlowType t0 |
revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
|
simpleLocalFlowStep(node, mid) and
t = t0
or
exists(boolean preservesValue |
additionalLambdaFlowStep(node, mid, preservesValue) and
getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
|
preservesValue = false and
t = getNodeType(node)
or
preservesValue = true and
t = t0
)
)
or
// jump step
exists(Node mid, DataFlowType t0 |
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
toReturn = false and
toJump = true and
lastCall = TDataFlowCallNone()
|
jumpStep(node, mid) and
t = t0
or
exists(boolean preservesValue |
additionalLambdaFlowStep(node, mid, preservesValue) and
getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
|
preservesValue = false and
t = getNodeType(node)
or
preservesValue = true and
t = t0
)
)
or
// flow into a callable
exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
(
if lastCall0 = TDataFlowCallNone() and toJump = false
then lastCall = TDataFlowCallSome(call)
else lastCall = lastCall0
) and
toReturn = false
|
viableParamArgNonLambda(call, p, node)
or
viableParamArgLambda(call, p, node) // non-linear recursion
)
or
// flow out of a callable
exists(TReturnPositionSimple pos |
revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
toReturn = true
)
}
pragma[nomagic]
predicate revLambdaFlowOutLambdaCall(
DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
DataFlowCall call, DataFlowCallOption lastCall
) {
revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
exists(ReturnKindExt rk |
out = rk.getAnOutNode(call) and
lambdaCall(call, _, _)
)
}
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
boolean toJump, DataFlowCallOption lastCall
) {
exists(DataFlowCall call, OutNode out |
revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
viableReturnPosOutNonLambda(call, pos, out)
or
// non-linear recursion
revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
viableReturnPosOutLambda(call, _, pos, out)
)
}
pragma[nomagic]
predicate revLambdaFlowIn(
DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
DataFlowCallOption lastCall
) {
revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
}
}
private DataFlowCallable viableCallableExt(DataFlowCall call) {
result = viableCallable(call)
or
result = viableCallableLambda(call, _)
}
cached
private module Cached {
/**
* Gets a viable target for the lambda call `call`.
*
* `lastCall` records the call required to reach `call` in order for the result
* to be a viable target, if any.
*/
cached
DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
exists(Node creation, LambdaCallKind kind |
LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
lambdaCreation(creation, kind, result)
)
}
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
p.isParameterOf(viableCallable(call), i)
p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
viableCallable(call) = result.getCallable() and
viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
pragma[nomagic]
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
mayBenefitFromCallContext(call, callable)
or
callable = call.getEnclosingCallable() and
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
}
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
pragma[nomagic]
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx)
or
result = viableCallableLambda(call, TDataFlowCallSome(ctx))
or
exists(DataFlowCallable enclosing |
mayBenefitFromCallContextExt(call, enclosing) and
enclosing = viableCallableExt(ctx) and
result = viableCallableLambda(call, TDataFlowCallNone())
)
}
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
mayBenefitFromCallContext(call, c) and
c = viableCallable(ctx) and
ctxtgts = count(viableImplInCallContext(call, ctx)) and
tgts = strictcount(viableCallable(call)) and
mayBenefitFromCallContextExt(call, c) and
c = viableCallableExt(ctx) and
ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
mayBenefitFromCallContext(call, _) and
c = viableCallable(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
mayBenefitFromCallContextExt(call, _) and
c = viableCallableExt(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -481,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
cached
newtype TDataFlowCallOption =
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
cc instanceof CallContextAny and callable = viableCallable(call)
cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
else result = viableCallable(call)
else result = viableCallableExt(call)
)
or
result = viableCallable(call) and cc instanceof CallContextSomeCall
result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
result = viableCallable(call) and cc instanceof CallContextAny
result = viableCallableExt(call) and cc instanceof CallContextAny
or
result = viableCallable(call) and cc instanceof CallContextReturn
result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
/** An optional `DataFlowCall`. */
class DataFlowCallOption extends TDataFlowCallOption {
string toString() {
this = TDataFlowCallNone() and
result = "(none)"
or
exists(DataFlowCall call |
this = TDataFlowCallSome(call) and
result = call.toString()
)
}
}
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -5,7 +5,6 @@ private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon
private import ControlFlowReachability
private import DelegateDataFlow
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.Caching
@@ -292,8 +291,8 @@ module LocalFlow {
*/
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
exists(ControlFlow::BasicBlock bb, int i | SsaImpl::lastRefBeforeRedef(def, bb, i, next) |
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
def.definesAt(_, bb, i)
def.definesAt(_, bb, i) and
def = getSsaDefinition(nodeFrom)
or
nodeFrom.asExprAtNode(bb.getNode(i)) instanceof AssignableRead
)
@@ -433,7 +432,7 @@ private class Argument extends Expr {
this = dc.getQualifier() and arg = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
).getCall()
or
this = call.(DelegateCall).getArgument(arg)
this = call.(DelegateLikeCall).getArgument(arg)
}
/**
@@ -2022,3 +2021,69 @@ class Unit extends TUnit {
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) { none() }
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a delegate for `c`. */
predicate lambdaCreation(ExprNode creation, LambdaCallKind kind, DataFlowCallable c) {
exists(Expr e | e = creation.getExpr() |
c = e.(AnonymousFunctionExpr)
or
c = e.(CallableAccess).getTarget().getUnboundDeclaration()
or
c = e.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
) and
kind = TMkUnit()
}
private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
LambdaConfiguration() { this = "LambdaConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1 = e2.(DelegateLikeCall).getExpr() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
e1 = e2.(DelegateCreation).getArgument() and
exactScope = false and
scope = e2 and
isSuccessor = true
}
}
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
exists(LambdaConfiguration x, DelegateLikeCall dc |
x.hasExprPath(dc.getExpr(), receiver.(ExprNode).getControlFlowNode(), dc,
call.getControlFlowNode())
)
or
receiver = call.(SummaryDelegateCall).getParameterNode()
) and
kind = TMkUnit()
}
/** Extra data-flow steps needed for lamba flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
exists(Ssa::Definition def |
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
LocalFlow::usesInstanceField(def) and
preservesValue = true
)
or
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode()) and
preservesValue = false
)
or
exists(AddEventExpr aee |
nodeFrom.asExpr() = aee.getRValue() and
nodeTo.asExpr().(EventRead).getTarget() = aee.getTarget() and
preservesValue = false
)
}

View File

@@ -1,4 +1,6 @@
/**
* DEPRECATED.
*
* INTERNAL: Do not use.
*
* Provides classes for resolving delegate calls.
@@ -108,7 +110,7 @@ abstract private class DelegateLikeFlowSink extends DataFlow::Node {
* possible delegates resolved on line 6.
*/
cached
Callable getARuntimeTarget(CallContext context) {
deprecated Callable getARuntimeTarget(CallContext context) {
exists(DelegateLikeFlowSource dfs |
flowsFrom(this, dfs, _, context) and
result = dfs.getCallable()
@@ -117,7 +119,7 @@ abstract private class DelegateLikeFlowSink extends DataFlow::Node {
}
/** A delegate or function pointer call expression. */
class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
deprecated class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
DelegateLikeCall dc;
DelegateLikeCallExpr() { this.getExpr() = dc.getExpr() }
@@ -126,16 +128,8 @@ class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
DelegateLikeCall getCall() { result = dc }
}
/** A parameter of delegate type belonging to a callable with a flow summary. */
class SummaryDelegateParameterSink extends DelegateLikeFlowSink, ParameterNode {
SummaryDelegateParameterSink() {
this.getType() instanceof SystemLinqExpressions::DelegateExtType and
this.isParameterOf(any(SummarizedCallable c), _)
}
}
/** A delegate expression that is added to an event. */
class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
deprecated class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
AddEventExpr ae;
AddEventSource() { this.getExpr() = ae.getRValue() }
@@ -173,7 +167,7 @@ private class NormalReturnNode extends Node {
* `node` goes through a returned expression. The call context `lastCall`
* records the last call on the path from `node` to `sink`, if any.
*/
private predicate flowsFrom(
deprecated private predicate flowsFrom(
DelegateLikeFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
) {
// Base case
@@ -244,7 +238,7 @@ private predicate flowsFrom(
* previous call if it exists, otherwise `call` is the last call.
*/
bindingset[call, i]
private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
deprecated private CallContext getLastCall(CallContext prevLastCall, Expr call, int i) {
prevLastCall instanceof EmptyCallContext and
result.(ArgumentCallContext).isArgument(call, i)
or
@@ -263,7 +257,7 @@ private predicate flowIntoNonDelegateCall(NonDelegateCall call, Expr arg, DotNet
}
pragma[noinline]
private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
deprecated private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce |
// the call context is irrelevant because the delegate call
// itself will be the context
@@ -280,7 +274,7 @@ private predicate flowOutOfNonDelegateCall(NonDelegateCall call, NormalReturnNod
}
pragma[noinline]
private predicate flowOutOfDelegateCall(
deprecated private predicate flowOutOfDelegateCall(
DelegateLikeCall dc, NormalReturnNode ret, CallContext lastCall
) {
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce, Callable c |

View File

@@ -235,7 +235,6 @@ private module CallGraph {
}
private module SimpleDelegateAnalysis {
private import semmle.code.csharp.dataflow.internal.DelegateDataFlow
private import semmle.code.csharp.dataflow.internal.Steps
private import semmle.code.csharp.frameworks.system.linq.Expressions

View File

@@ -8,6 +8,8 @@ import Expr
import semmle.code.csharp.Callable
import semmle.code.csharp.dataflow.CallContext as CallContext
private import semmle.code.csharp.dataflow.internal.DelegateDataFlow
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
private import semmle.code.csharp.dispatch.Dispatch
private import dotnet
@@ -536,10 +538,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
override Callable getTarget() { none() }
/**
* DEPRECATED: Use `getARuntimeTarget/0` instead.
*
* Gets a potential run-time target of this delegate or function pointer call in the given
* call context `cc`.
*/
Callable getARuntimeTarget(CallContext::CallContext cc) {
deprecated Callable getARuntimeTarget(CallContext::CallContext cc) {
exists(DelegateLikeCallExpr call |
this = call.getCall() and
result = call.getARuntimeTarget(cc)
@@ -562,7 +566,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
*/
Expr getExpr() { result = this.getChild(-1) }
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
final override Callable getARuntimeTarget() {
exists(ExplicitDelegateLikeDataFlowCall call |
this = call.getCall() and
result = viableCallableLambda(call, _)
)
}
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
}
@@ -582,10 +591,12 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ {
*/
class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
/**
* DEPRECATED: Use `getARuntimeTarget/0` instead.
*
* Gets a potential run-time target of this delegate call in the given
* call context `cc`.
*/
override Callable getARuntimeTarget(CallContext::CallContext cc) {
deprecated override Callable getARuntimeTarget(CallContext::CallContext cc) {
result = DelegateLikeCall.super.getARuntimeTarget(cc)
or
exists(AddEventSource aes, CallContext::CallContext cc2 |
@@ -601,16 +612,16 @@ class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
)
}
private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
deprecated private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
this.getExpr().(EventAccess).getTarget() = result.getEvent() and
enclosingCallable = result.getExpr().getEnclosingCallable()
}
private AddEventSource getAnAddEventSourceSameEnclosingCallable() {
deprecated private AddEventSource getAnAddEventSourceSameEnclosingCallable() {
result = getAnAddEventSource(this.getEnclosingCallable())
}
private AddEventSource getAnAddEventSourceDifferentEnclosingCallable() {
deprecated private AddEventSource getAnAddEventSourceDifferentEnclosingCallable() {
exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable())
}

View File

@@ -124,4 +124,13 @@ class DelegateFlow
delegate*<Action<int>, void> fnptr = &M2;
fnptr((i) => { });
}
void M19(Action a, bool b)
{
if (b)
a = () => {};
a();
}
void M20(bool b) => M19(() => {}, b);
}

View File

@@ -1,26 +1,55 @@
summaryDelegateCall
| file://:0:0:0:0 | valueFactory | DelegateFlow.cs:104:23:104:30 | (...) => ... | DelegateFlow.cs:105:23:105:23 | access to local variable f |
| file://:0:0:0:0 | valueFactory | DelegateFlow.cs:106:13:106:20 | (...) => ... | DelegateFlow.cs:107:23:107:23 | access to local variable f |
delegateCall
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:17:12:17:13 | delegate creation of type Action<Int32> |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:22:12:22:12 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... | DelegateFlow.cs:22:12:22:12 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction | DelegateFlow.cs:99:12:99:24 | delegate creation of type Action<Int32> |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... | DelegateFlow.cs:114:15:114:15 | access to parameter a |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... | DelegateFlow.cs:125:15:125:24 | (...) => ... |
| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... | DelegateFlow.cs:38:12:38:25 | (...) => ... |
| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:33:12:33:12 | access to parameter a |
| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:43:22:43:29 | (...) => ... | DelegateFlow.cs:50:18:50:23 | dynamic access to member Prop |
| DelegateFlow.cs:57:9:57:11 | delegate call | DelegateFlow.cs:53:34:53:47 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:57:9:57:14 | delegate call | DelegateFlow.cs:53:40:53:47 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:67:9:67:16 | delegate call | DelegateFlow.cs:62:16:62:23 | (...) => ... | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | DelegateFlow.cs:78:13:78:14 | delegate creation of type EventHandler |
| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | file://:0:0:0:0 | <empty> |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 | DelegateFlow.cs:90:13:90:30 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 | DelegateFlow.cs:91:13:91:47 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 | DelegateFlow.cs:92:13:92:15 | delegate creation of type MyDelegate |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:27:12:27:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:98:9:98:37 | LocalFunction |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:119:18:119:27 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:15:125:24 | (...) => ... |
| DelegateFlow.cs:11:9:11:12 | delegate call | DelegateFlow.cs:10:13:10:20 | (...) => ... |
| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:12:38:25 | (...) => ... |
| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:43:22:43:29 | (...) => ... |
| DelegateFlow.cs:57:9:57:11 | delegate call | DelegateFlow.cs:53:34:53:47 | (...) => ... |
| DelegateFlow.cs:57:9:57:14 | delegate call | DelegateFlow.cs:53:40:53:47 | (...) => ... |
| DelegateFlow.cs:67:9:67:16 | delegate call | DelegateFlow.cs:62:16:62:23 | (...) => ... |
| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:77:9:77:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:74:17:74:19 | M12 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:36 | (...) => ... |
viableLambda
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:9:16:20 | call to method M2 | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| 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 |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:22:9:22:13 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:22:9:22:13 | call to method M2 | DelegateFlow.cs:27:12:27:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:99:9:99:25 | call to method M2 | DelegateFlow.cs:98:9:98:37 | LocalFunction |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:119:18:119:27 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:125:15:125:24 | (...) => ... |
| DelegateFlow.cs:11:9:11:12 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:10:13:10:20 | (...) => ... |
| DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:38:9:38:30 | call to method M6 | DelegateFlow.cs:38:12:38:25 | (...) => ... |
| DelegateFlow.cs:38:19:38:22 | delegate call | DelegateFlow.cs:33:9:33:13 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 |
| DelegateFlow.cs:44:15:44:22 | delegate call | DelegateFlow.cs:50:9:50:14 | dynamic access to member Prop | DelegateFlow.cs:43:22:43:29 | (...) => ... |
| DelegateFlow.cs:57:9:57:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:53:34:53:47 | (...) => ... |
| DelegateFlow.cs:57:9:57:14 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:53:40:53:47 | (...) => ... |
| DelegateFlow.cs:67:9:67:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:62:16:62:23 | (...) => ... |
| DelegateFlow.cs:77:9:77:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:77:9:77:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:84:9:84:15 | delegate call | DelegateFlow.cs:78:9:78:15 | call to method M13 | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:84:9:84:15 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:90:9:90:31 | call to local function M14 | DelegateFlow.cs:55:10:55:11 | M9 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:91:9:91:48 | call to local function M14 | DelegateFlow.cs:65:10:65:12 | M11 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:92:9:92:16 | call to local function M14 | DelegateFlow.cs:74:17:74:19 | M12 |
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:9:93:22 | call to local function M14 | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:119:9:119:28 | call to method M16 | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:125:9:125:25 | function pointer call | file://:0:0:0:0 | (none) | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:40 | call to method M19 | DelegateFlow.cs:135:29:135:36 | (...) => ... |
| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:24 | (...) => ... |
| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | DelegateFlow.cs:105:9:105:24 | object creation of type Lazy<Int32> | DelegateFlow.cs:104:23:104:30 | (...) => ... |
| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | DelegateFlow.cs:107:9:107:24 | object creation of type Lazy<Int32> | DelegateFlow.cs:106:13:106:20 | (...) => ... |

View File

@@ -1,22 +1,34 @@
import csharp
import semmle.code.csharp.dataflow.internal.DataFlowPrivate
import semmle.code.csharp.dataflow.internal.DelegateDataFlow
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private class NodeAdjusted extends TNode {
string toString() { result = this.(DataFlow::Node).toString() }
query predicate delegateCall(DelegateLikeCall dc, Callable c) { c = dc.getARuntimeTarget() }
private class LocatableDataFlowCallOption extends DataFlowCallOption {
Location getLocation() {
this = TDataFlowCallNone() and
result instanceof EmptyLocation
or
exists(DataFlowCall call |
this = TDataFlowCallSome(call) and
result = call.getLocation()
)
}
}
private class LocatableDataFlowCall extends TDataFlowCall {
string toString() { result = this.(DataFlowCall).toString() }
Location getLocation() {
exists(Location l |
l = this.(DataFlow::Node).getLocation() and
l = this.(DataFlowCall).getLocation() and
if l instanceof SourceLocation then result = l else result instanceof EmptyLocation
)
}
}
query predicate summaryDelegateCall(NodeAdjusted sink, Callable c, CallContext::CallContext cc) {
c = sink.(SummaryDelegateParameterSink).getARuntimeTarget(cc)
}
query predicate delegateCall(DelegateCall dc, Callable c, CallContext::CallContext cc) {
c = dc.getARuntimeTarget(cc)
query predicate viableLambda(
LocatableDataFlowCall call, LocatableDataFlowCallOption lastCall, DataFlowCallable target
) {
target = viableCallableLambda(call, lastCall)
}

View File

@@ -1,9 +1,20 @@
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:5:24:5:27 | Log1 | FunctionPointerFlow.cs:21:12:21:16 | &... |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:6:24:6:27 | Log2 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:10:24:10:27 | Log6 | FunctionPointerFlow.cs:26:12:26:12 | access to parameter a |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction | FunctionPointerFlow.cs:47:12:47:25 | &... |
| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | FunctionPointerFlow.cs:7:24:7:27 | Log3 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | FunctionPointerFlow.cs:8:24:8:27 | Log4 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | FunctionPointerFlow.cs:9:24:9:27 | Log5 | file://:0:0:0:0 | <empty> |
| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:24:24:24:25 | M4 | FunctionPointerFlow.cs:64:13:64:15 | &... |
| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:72:24:72:26 | M17 | FunctionPointerFlow.cs:81:13:81:16 | &... |
fptrCall
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:5:24:5:27 | Log1 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:6:24:6:27 | Log2 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:10:24:10:27 | Log6 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction |
| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | FunctionPointerFlow.cs:7:24:7:27 | Log3 |
| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | FunctionPointerFlow.cs:8:24:8:27 | Log4 |
| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | FunctionPointerFlow.cs:9:24:9:27 | Log5 |
| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:24:24:24:25 | M4 |
| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:72:24:72:26 | M17 |
fptrCallContext
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:21:9:21:17 | call to method M2 | FunctionPointerFlow.cs:5:24:5:27 | Log1 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:26:9:26:13 | call to method M2 | FunctionPointerFlow.cs:6:24:6:27 | Log2 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:26:9:26:13 | call to method M2 | FunctionPointerFlow.cs:10:24:10:27 | Log6 |
| FunctionPointerFlow.cs:14:9:14:12 | function pointer call | FunctionPointerFlow.cs:47:9:47:26 | call to method M2 | FunctionPointerFlow.cs:46:9:46:44 | LocalFunction |
| FunctionPointerFlow.cs:16:9:16:12 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:7:24:7:27 | Log3 |
| FunctionPointerFlow.cs:41:9:41:15 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:8:24:8:27 | Log4 |
| FunctionPointerFlow.cs:54:9:54:16 | function pointer call | file://:0:0:0:0 | (none) | FunctionPointerFlow.cs:9:24:9:27 | Log5 |
| FunctionPointerFlow.cs:59:9:59:13 | function pointer call | FunctionPointerFlow.cs:64:9:64:23 | call to method M10 | FunctionPointerFlow.cs:24:24:24:25 | M4 |
| FunctionPointerFlow.cs:69:9:69:13 | function pointer call | FunctionPointerFlow.cs:81:9:81:29 | call to method M16 | FunctionPointerFlow.cs:72:24:72:26 | M17 |

View File

@@ -1,5 +1,38 @@
import csharp
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
import semmle.code.csharp.dataflow.internal.DataFlowDispatch
query predicate fptrCall(FunctionPointerCall fptrc, Callable c, CallContext::CallContext cc) {
c = fptrc.getARuntimeTarget(cc)
query predicate fptrCall(FunctionPointerCall dc, Callable c) { c = dc.getARuntimeTarget() }
private class LocatableDataFlowCallOption extends DataFlowCallOption {
Location getLocation() {
this = TDataFlowCallNone() and
result instanceof EmptyLocation
or
exists(DataFlowCall call |
this = TDataFlowCallSome(call) and
result = call.getLocation()
)
}
}
private class LocatableDataFlowCall extends TDataFlowCall {
LocatableDataFlowCall() {
this.(ExplicitDelegateLikeDataFlowCall).getCall() instanceof FunctionPointerCall
}
string toString() { result = this.(DataFlowCall).toString() }
Location getLocation() {
exists(Location l |
l = this.(DataFlowCall).getLocation() and
if l instanceof SourceLocation then result = l else result instanceof EmptyLocation
)
}
}
query predicate fptrCallContext(
LocatableDataFlowCall call, LocatableDataFlowCallOption lastCall, DataFlowCallable target
) {
target = viableCallableLambda(call, lastCall)
}

View File

@@ -52,6 +52,7 @@
| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 |
| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x |

View File

@@ -229,6 +229,10 @@ edges
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
| GlobalDataFlow.cs:480:53:480:55 | arg : String | GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String |
| GlobalDataFlow.cs:483:21:483:21 | s : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | GlobalDataFlow.cs:483:21:483:21 | s : String |
| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:480:53:480:55 | arg : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
@@ -444,6 +448,11 @@ nodes
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | semmle.label | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | semmle.label | call to method GetResult : String |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | semmle.label | access to local variable sink45 |
| GlobalDataFlow.cs:480:53:480:55 | arg : String | semmle.label | arg : String |
| GlobalDataFlow.cs:483:21:483:21 | s : String | semmle.label | s : String |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | semmle.label | access to parameter s |
| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | semmle.label | access to parameter arg : String |
| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | semmle.label | "taint source" : String |
| Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String |
@@ -520,6 +529,7 @@ nodes
| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 |
| Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x |
| Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s | access to parameter s |
| Capture.cs:57:27:57:32 | access to parameter sink39 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:57:27:57:32 | access to parameter sink39 | access to parameter sink39 |
| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | access to parameter sinkParam0 |
| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | access to parameter sinkParam1 |

View File

@@ -196,7 +196,7 @@
| GlobalDataFlow.cs:472:25:472:50 | call to method ConfigureAwait | return | GlobalDataFlow.cs:472:25:472:50 | call to method ConfigureAwait |
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter | return | GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult | return | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult |
| GlobalDataFlow.cs:486:44:486:47 | delegate call | return | GlobalDataFlow.cs:486:44:486:47 | delegate call |
| GlobalDataFlow.cs:498:44:498:47 | delegate call | return | GlobalDataFlow.cs:498:44:498:47 | delegate call |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return |
| Splitting.cs:20:22:20:30 | call to method Return | return | Splitting.cs:20:22:20:30 | call to method Return |

View File

@@ -474,6 +474,18 @@ public class DataFlow
var sink45 = awaiter.GetResult();
Check(sink45);
}
void M5(bool b)
{
void Inner(Action<string> a, bool b, string arg)
{
if (b)
a = s => Check(s);
a(arg);
}
Inner(_ => {}, b, "taint source");
}
}
static class IEnumerableExtensions

View File

@@ -58,6 +58,7 @@
| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 |
| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x |

View File

@@ -249,6 +249,10 @@ edges
| GlobalDataFlow.cs:473:23:473:44 | call to method GetAwaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 |
| GlobalDataFlow.cs:480:53:480:55 | arg : String | GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String |
| GlobalDataFlow.cs:483:21:483:21 | s : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s |
| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | GlobalDataFlow.cs:483:21:483:21 | s : String |
| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:480:53:480:55 | arg : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String |
| Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
@@ -486,6 +490,11 @@ nodes
| GlobalDataFlow.cs:474:22:474:28 | access to local variable awaiter [m_task, Result] : String | semmle.label | access to local variable awaiter [m_task, Result] : String |
| GlobalDataFlow.cs:474:22:474:40 | call to method GetResult : String | semmle.label | call to method GetResult : String |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | semmle.label | access to local variable sink45 |
| GlobalDataFlow.cs:480:53:480:55 | arg : String | semmle.label | arg : String |
| GlobalDataFlow.cs:483:21:483:21 | s : String | semmle.label | s : String |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | semmle.label | access to parameter s |
| GlobalDataFlow.cs:484:15:484:17 | access to parameter arg : String | semmle.label | access to parameter arg : String |
| GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | semmle.label | "taint source" : String |
| Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String |
| Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String |
| Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String |
@@ -573,6 +582,7 @@ nodes
| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | access to local variable sink43 |
| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | access to local variable sink44 |
| GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | GlobalDataFlow.cs:471:35:471:48 | "taint source" : String | GlobalDataFlow.cs:475:15:475:20 | access to local variable sink45 | access to local variable sink45 |
| GlobalDataFlow.cs:483:32:483:32 | access to parameter s | GlobalDataFlow.cs:487:27:487:40 | "taint source" : String | GlobalDataFlow.cs:483:32:483:32 | access to parameter s | access to parameter s |
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x |
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x |
| Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x |