Merge pull request #14295 from hvitved/csharp/lambda-type-flow

C#: Improve lambda dispatch using type flow
This commit is contained in:
Tom Hvitved
2023-09-25 19:19:51 +02:00
committed by GitHub
11 changed files with 5766 additions and 73 deletions

View File

@@ -47,6 +47,7 @@ extensions:
- ["System.Collections.Generic", "List<>", False, "FindAll", "(System.Predicate<T>)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "FindLast", "(System.Predicate<T>)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "FindLast", "(System.Predicate<T>)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "ForEach", "(System.Action<T>)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "GetEnumerator", "()", "", "Argument[this].Element", "ReturnValue.Property[System.Collections.Generic.List<>+Enumerator.Current]", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "GetRange", "(System.Int32,System.Int32)", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"]
- ["System.Collections.Generic", "List<>", False, "InsertRange", "(System.Int32,System.Collections.Generic.IEnumerable<T>)", "", "Argument[1].Element", "Argument[this].Element", "value", "manual"]

View File

@@ -4,6 +4,7 @@ import csharp
private import dotnet
private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowDispatch as DataFlowDispatch
private import Impl::Public::SummaryComponent as SummaryComponentInternal
class ParameterPosition = DataFlowDispatch::ParameterPosition;
@@ -18,8 +19,6 @@ class SummaryComponent = Impl::Public::SummaryComponent;
/** Provides predicates for constructing summary components. */
module SummaryComponent {
private import Impl::Public::SummaryComponent as SummaryComponentInternal
predicate content = SummaryComponentInternal::content/1;
/** Gets a summary component for parameter `i`. */
@@ -155,3 +154,45 @@ private class RecordConstructorFlowRequiredSummaryComponentStack extends Require
)
}
}
class Provenance = Impl::Public::Provenance;
private import semmle.code.csharp.frameworks.system.linq.Expressions
private SummaryComponent delegateSelf() {
exists(ArgumentPosition pos |
result = SummaryComponentInternal::parameter(pos) and
pos.isDelegateSelf()
)
}
private predicate mayInvokeCallback(Callable c, int n) {
c.getParameter(n).getType() instanceof SystemLinqExpressions::DelegateExtType and
not c.fromSource()
}
private class SummarizedCallableWithCallback extends SummarizedCallable {
private int pos;
SummarizedCallableWithCallback() { mayInvokeCallback(this, pos) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = SummaryComponentStack::argument(pos) and
output = SummaryComponentStack::push(delegateSelf(), input) and
preservesValue = true
}
override predicate hasProvenance(Provenance provenance) { provenance = "hq-generated" }
}
private class RequiredComponentStackForCallback extends RequiredSummaryComponentStack {
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
exists(int pos |
mayInvokeCallback(_, pos) and
head = delegateSelf() and
tail = SummaryComponentStack::argument(pos)
)
}
}

View File

@@ -136,13 +136,15 @@ private module Cached {
newtype TParameterPosition =
TPositionalParameterPosition(int i) { i = any(Parameter p).getPosition() } or
TThisParameterPosition() or
TImplicitCapturedParameterPosition(LocalScopeVariable v) { capturedWithFlowIn(v) }
TImplicitCapturedParameterPosition(LocalScopeVariable v) { capturedWithFlowIn(v) } or
TDelegateSelfParameterPosition()
cached
newtype TArgumentPosition =
TPositionalArgumentPosition(int i) { i = any(Parameter p).getPosition() } or
TQualifierArgumentPosition() or
TImplicitCapturedArgumentPosition(LocalScopeVariable v) { capturedWithFlowIn(v) }
TImplicitCapturedArgumentPosition(LocalScopeVariable v) { capturedWithFlowIn(v) } or
TDelegateSelfArgumentPosition()
}
import Cached
@@ -480,6 +482,14 @@ class ParameterPosition extends TParameterPosition {
this = TImplicitCapturedParameterPosition(v)
}
/**
* Holds if this position represents a reference to a delegate itself.
*
* Used for tracking flow through captured variables and for improving
* delegate dispatch.
*/
predicate isDelegateSelf() { this = TDelegateSelfParameterPosition() }
/** Gets a textual representation of this position. */
string toString() {
result = "position " + this.getPosition()
@@ -489,6 +499,9 @@ class ParameterPosition extends TParameterPosition {
exists(LocalScopeVariable v |
this.isImplicitCapturedParameterPosition(v) and result = "captured " + v
)
or
this.isDelegateSelf() and
result = "delegate self"
}
}
@@ -505,6 +518,14 @@ class ArgumentPosition extends TArgumentPosition {
this = TImplicitCapturedArgumentPosition(v)
}
/**
* Holds if this position represents a reference to a delegate itself.
*
* Used for tracking flow through captured variables and for improving
* delegate dispatch.
*/
predicate isDelegateSelf() { this = TDelegateSelfArgumentPosition() }
/** Gets a textual representation of this position. */
string toString() {
result = "position " + this.getPosition()
@@ -514,6 +535,9 @@ class ArgumentPosition extends TArgumentPosition {
exists(LocalScopeVariable v |
this.isImplicitCapturedArgumentPosition(v) and result = "captured " + v
)
or
this.isDelegateSelf() and
result = "delegate self"
}
}
@@ -527,4 +551,6 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
ppos.isImplicitCapturedParameterPosition(v) and
apos.isImplicitCapturedArgumentPosition(v)
)
or
ppos.isDelegateSelf() and apos.isDelegateSelf()
}

View File

@@ -45,9 +45,9 @@ abstract class NodeImpl extends Node {
abstract DotNet::Type getTypeImpl();
/** Gets the type of this node used for type pruning. */
Gvn::GvnType getDataFlowType() {
DataFlowType getDataFlowType() {
forceCachingInSameStage() and
exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) |
exists(Type t0 | result.asGvnType() = Gvn::getGlobalValueNumber(t0) |
t0 = getCSharpType(this.getType())
or
not exists(getCSharpType(this.getType())) and
@@ -557,6 +557,8 @@ module LocalFlow {
exists(SsaImpl::getAReadAtNode(def, node2.(ExprNode).getControlFlowNode()))
)
or
delegateCreationStep(node1, node2)
or
node1 =
unique(FlowSummaryNode n1 |
FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(),
@@ -793,16 +795,16 @@ private Type getCSharpType(DotNet::Type t) {
result.matchesHandle(t)
}
private class RelevantDataFlowType extends DataFlowType {
RelevantDataFlowType() { this = any(NodeImpl n).getDataFlowType() }
private class RelevantGvnType extends Gvn::GvnType {
RelevantGvnType() { this = any(NodeImpl n).getDataFlowType().asGvnType() }
}
/** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */
private class DataFlowTypeOrUnifiable extends Gvn::GvnType {
pragma[nomagic]
DataFlowTypeOrUnifiable() {
this instanceof RelevantDataFlowType or
Gvn::unifiable(any(RelevantDataFlowType t), this)
this instanceof RelevantGvnType or
Gvn::unifiable(any(RelevantGvnType t), this)
}
}
@@ -813,7 +815,7 @@ private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) {
}
pragma[noinline]
private TypeParameter getATypeParameterSubTypeRestricted(RelevantDataFlowType t) {
private TypeParameter getATypeParameterSubTypeRestricted(RelevantGvnType t) {
result = getATypeParameterSubType(t)
}
@@ -829,7 +831,7 @@ private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) {
}
pragma[noinline]
private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantDataFlowType t) {
private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantGvnType t) {
result = getANonTypeParameterSubType(t)
}
@@ -866,6 +868,7 @@ private module Cached {
c = any(DataFlowCallable dfc).asCallable() and
not c.(Modifiable).isStatic()
} or
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
TYieldReturnNode(ControlFlow::Nodes::ElementNode cfn) {
any(Callable c).canYieldReturn(cfn.getAstNode())
} or
@@ -949,7 +952,7 @@ private module Cached {
TSyntheticFieldApproxContent()
pragma[nomagic]
private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantDataFlowType t2) {
private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantGvnType t2) {
not t1 instanceof Gvn::TypeParameterGvnType and
t1 = t2
or
@@ -963,17 +966,20 @@ private module Cached {
* `t2` are allowed to be type parameters.
*/
cached
predicate commonSubType(RelevantDataFlowType t1, RelevantDataFlowType t2) {
commonSubTypeGeneral(t1, t2)
}
predicate commonSubType(RelevantGvnType t1, RelevantGvnType t2) { commonSubTypeGeneral(t1, t2) }
cached
predicate commonSubTypeUnifiableLeft(RelevantDataFlowType t1, RelevantDataFlowType t2) {
predicate commonSubTypeUnifiableLeft(RelevantGvnType t1, RelevantGvnType t2) {
exists(Gvn::GvnType t |
Gvn::unifiable(t1, t) and
commonSubTypeGeneral(t, t2)
)
}
cached
newtype TDataFlowType =
TGvnDataFlowType(Gvn::GvnType t) or
TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) }
}
import Cached
@@ -1119,6 +1125,37 @@ private module ParameterNodes {
override string toStringImpl() { result = "this" }
}
/**
* The value of a delegate itself at function entry, viewed as a node in a data
* flow graph.
*
* This is used for improving lambda dispatch, and will eventually also be
* used for tracking flow through captured variables.
*/
private class DelegateSelfReferenceNode extends ParameterNodeImpl, TDelegateSelfReferenceNode {
private Callable callable;
DelegateSelfReferenceNode() { this = TDelegateSelfReferenceNode(callable) }
final Callable getCallable() { result = callable }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
callable = c.asCallable() and pos.isDelegateSelf()
}
override ControlFlow::Node getControlFlowNodeImpl() { none() }
override DataFlowCallable getEnclosingCallableImpl() { result.asCallable() = callable }
override Location getLocationImpl() { result = callable.getLocation() }
override DotNet::Type getTypeImpl() { none() }
override DataFlowType getDataFlowType() { callable = result.asDelegate() }
override string toStringImpl() { result = "delegate self in " + callable }
}
/** An implicit entry definition for a captured variable. */
class SsaCapturedEntryDefinition extends Ssa::ImplicitEntryDefinition {
private LocalScopeVariable v;
@@ -1232,6 +1269,18 @@ private module ArgumentNodes {
}
}
/** A data-flow node that represents a delegate passed into itself. */
class DelegateSelfArgumentNode extends ArgumentNodeImpl {
private DataFlowCall call_;
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this) }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call = call_ and
pos.isDelegateSelf()
}
}
/**
* The value of a captured variable as an implicit argument of a call, viewed
* as a node in a data flow graph.
@@ -1985,41 +2034,92 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
* For example, `Func<T, int>` and `Func<S, int>` are mapped to the same
* `DataFlowType`, while `Func<T, int>` and `Func<string, int>` are not, because
* `string` is not a type parameter.
*
* For delegates, we use the delegate itself instead of its type, in order to
* improve dispatch.
*/
class DataFlowType = Gvn::GvnType;
class DataFlowType extends TDataFlowType {
Gvn::GvnType asGvnType() { this = TGvnDataFlowType(result) }
Callable asDelegate() { this = TDelegateDataFlowType(result) }
/**
* Gets an expression that creates a delegate of this type.
*
* For methods used as method groups in calls there can be multiple
* creations associated with the same type.
*/
Expr getADelegateCreation() {
exists(Callable callable |
lambdaCreationExpr(result, callable) and
this = TDelegateDataFlowType(callable)
)
}
final string toString() {
result = this.asGvnType().toString()
or
result = this.asDelegate().toString()
}
}
/** Gets the type of `n` used for type pruning. */
Gvn::GvnType getNodeType(Node n) { result = n.(NodeImpl).getDataFlowType() }
DataFlowType getNodeType(Node n) {
result = n.(NodeImpl).getDataFlowType() and
not lambdaCreation(n, _, _) and
not delegateCreationStep(_, n)
or
exists(Node arg |
delegateCreationStep(arg, n) and
result = getNodeType(arg)
)
or
n.asExpr() = result.getADelegateCreation()
}
/** Gets a string representation of a `DataFlowType`. */
string ppReprType(DataFlowType t) { result = t.toString() }
private class DataFlowNullType extends DataFlowType {
private class DataFlowNullType extends Gvn::GvnType {
DataFlowNullType() { this = Gvn::getGlobalValueNumber(any(NullType nt)) }
pragma[noinline]
predicate isConvertibleTo(DataFlowType t) {
predicate isConvertibleTo(Gvn::GvnType t) {
defaultNullConversion(_, any(Type t0 | t = Gvn::getGlobalValueNumber(t0)))
}
}
private class DataFlowUnknownType extends DataFlowType {
DataFlowUnknownType() { this = Gvn::getGlobalValueNumber(any(UnknownType ut)) }
}
private predicate uselessTypebound(DataFlowType t) {
t instanceof DataFlowUnknownType or
t instanceof Gvn::TypeParameterGvnType
private class GvnUnknownType extends Gvn::GvnType {
GvnUnknownType() { this = Gvn::getGlobalValueNumber(any(UnknownType ut)) }
}
pragma[nomagic]
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
t1 != t2 and
t1 = getANonTypeParameterSubTypeRestricted(t2)
or
t1 instanceof RelevantDataFlowType and
not uselessTypebound(t1) and
uselessTypebound(t2)
private predicate uselessTypebound(DataFlowType dt) {
dt.asGvnType() =
any(Gvn::GvnType t |
t instanceof GvnUnknownType or
t instanceof Gvn::TypeParameterGvnType
)
}
pragma[inline]
private predicate compatibleTypesDelegateLeft(DataFlowType dt1, DataFlowType dt2) {
exists(Gvn::GvnType t1, Gvn::GvnType t2 |
t1 = exprNode(dt1.getADelegateCreation()).(NodeImpl).getDataFlowType().asGvnType() and
t2 = dt2.asGvnType()
|
commonSubType(t1, t2)
or
commonSubTypeUnifiableLeft(t1, t2)
or
commonSubTypeUnifiableLeft(t2, t1)
or
t2.(DataFlowNullType).isConvertibleTo(t1)
or
t2 instanceof Gvn::TypeParameterGvnType
or
t2 instanceof GvnUnknownType
)
}
/**
@@ -2027,24 +2127,47 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
commonSubType(t1, t2)
predicate compatibleTypes(DataFlowType dt1, DataFlowType dt2) {
exists(Gvn::GvnType t1, Gvn::GvnType t2 |
t1 = dt1.asGvnType() and
t2 = dt2.asGvnType()
|
commonSubType(t1, t2)
or
commonSubTypeUnifiableLeft(t1, t2)
or
commonSubTypeUnifiableLeft(t2, t1)
or
t1.(DataFlowNullType).isConvertibleTo(t2)
or
t2.(DataFlowNullType).isConvertibleTo(t1)
or
t1 instanceof Gvn::TypeParameterGvnType
or
t2 instanceof Gvn::TypeParameterGvnType
or
t1 instanceof GvnUnknownType
or
t2 instanceof GvnUnknownType
)
or
commonSubTypeUnifiableLeft(t1, t2)
compatibleTypesDelegateLeft(dt1, dt2)
or
commonSubTypeUnifiableLeft(t2, t1)
compatibleTypesDelegateLeft(dt2, dt1)
or
t1.(DataFlowNullType).isConvertibleTo(t2)
dt1.asDelegate() = dt2.asDelegate()
}
pragma[nomagic]
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
t1 != t2 and
t1.asGvnType() = getANonTypeParameterSubTypeRestricted(t2.asGvnType())
or
t2.(DataFlowNullType).isConvertibleTo(t1)
t1.asGvnType() instanceof RelevantGvnType and
not uselessTypebound(t1) and
uselessTypebound(t2)
or
t1 instanceof Gvn::TypeParameterGvnType
or
t2 instanceof Gvn::TypeParameterGvnType
or
t1 instanceof DataFlowUnknownType
or
t2 instanceof DataFlowUnknownType
compatibleTypesDelegateLeft(t1, t2)
}
/**
@@ -2216,17 +2339,20 @@ int accessPathLimit() { result = 5 }
*/
predicate forceHighPrecision(Content c) { c instanceof ElementContent }
private predicate lambdaCreationExpr(Expr creation, Callable c) {
c =
[
creation.(AnonymousFunctionExpr),
creation.(CallableAccess).getTarget().getUnboundDeclaration(),
creation.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
]
}
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a delegate for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
exists(Expr e | e = creation.asExpr() |
c.asCallable() =
[
e.(AnonymousFunctionExpr), e.(CallableAccess).getTarget().getUnboundDeclaration(),
e.(AddressOfExpr).getOperand().(CallableAccess).getTarget().getUnboundDeclaration()
]
) and
lambdaCreationExpr(creation.asExpr(), c.asCallable()) and
exists(kind)
}
@@ -2248,19 +2374,29 @@ private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
}
}
private predicate lambdaCallExpr(DataFlowCall call, ExprNode receiver) {
exists(LambdaConfiguration x, DelegateLikeCall dc |
x.hasExprPath(dc.getExpr(), receiver.getControlFlowNode(), dc, call.getControlFlowNode())
)
}
/** 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())
)
lambdaCallExpr(call, receiver)
or
receiver.(FlowSummaryNode).getSummaryNode() = call.(SummaryCall).getReceiver()
) and
exists(kind)
}
private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode())
)
}
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
exists(SsaImpl::DefinitionExt def |
@@ -2269,11 +2405,8 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
preservesValue = true
)
or
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode()) and
preservesValue = false
)
delegateCreationStep(nodeFrom, nodeTo) and
preservesValue = true
or
exists(AddEventExpr aee |
nodeFrom.asExpr() = aee.getRValue() and

View File

@@ -35,14 +35,14 @@ private module SyntheticGlobals {
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
/** Gets the parameter position of the instance parameter. */
ArgumentPosition callbackSelfParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks
ArgumentPosition callbackSelfParameterPosition() { result.isDelegateSelf() }
/** Gets the synthesized data-flow call for `receiver`. */
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
/** Gets the type of content `c`. */
DataFlowType getContentType(Content c) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
t = c.(FieldContent).getField().getType()
or
t = c.(PropertyContent).getProperty().getType()
@@ -56,7 +56,7 @@ DataFlowType getContentType(Content c) {
/** Gets the type of the parameter at the given position. */
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
exists(int i |
pos.getPosition() = i and
t = c.getParameter(i).getType()
@@ -69,7 +69,7 @@ DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) {
/** Gets the return type of kind `rk` for callable `c`. */
DataFlowType getReturnType(DotNet::Callable c, ReturnKind rk) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
rk instanceof NormalReturnKind and
(
t = c.(Constructor).getDeclaringType()
@@ -88,10 +88,13 @@ DataFlowType getReturnType(DotNet::Callable c, ReturnKind rk) {
*/
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) {
exists(SystemLinqExpressions::DelegateExtType dt |
t = Gvn::getGlobalValueNumber(dt) and
result =
t.asGvnType() = Gvn::getGlobalValueNumber(dt) and
result.asGvnType() =
Gvn::getGlobalValueNumber(dt.getDelegateType().getParameter(pos.getPosition()).getType())
)
or
pos.isDelegateSelf() and
result = t
}
/**
@@ -101,15 +104,15 @@ DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) {
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
rk instanceof NormalReturnKind and
exists(SystemLinqExpressions::DelegateExtType dt |
t = Gvn::getGlobalValueNumber(dt) and
result = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
t.asGvnType() = Gvn::getGlobalValueNumber(dt) and
result.asGvnType() = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
)
}
/** Gets the type of synthetic global `sg`. */
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) {
exists(sg) and
result = Gvn::getGlobalValueNumber(any(ObjectType t))
result.asGvnType() = Gvn::getGlobalValueNumber(any(ObjectType t))
}
/**
@@ -223,6 +226,9 @@ string getParameterPosition(ParameterPosition pos) {
or
pos.isThisParameter() and
result = "this"
or
pos.isDelegateSelf() and
result = "delegate-self"
}
/** Gets the textual representation of an argument position in the format used for flow summaries. */
@@ -231,6 +237,9 @@ string getArgumentPosition(ArgumentPosition pos) {
or
pos.isQualifier() and
result = "this"
or
pos.isDelegateSelf() and
result = "delegate-self"
}
/** Holds if input specification component `c` needs a reference. */
@@ -312,6 +321,9 @@ ArgumentPosition parseParamBody(string s) {
or
s = "this" and
result.isQualifier()
or
s = "delegate-self" and
result.isDelegateSelf()
}
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
@@ -321,4 +333,7 @@ ParameterPosition parseArgBody(string s) {
or
s = "this" and
result.isThisParameter()
or
s = "delegate-self" and
result.isDelegateSelf()
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
public class A
{
static T Source<T>(T source) => throw null;
public static void Sink<T>(T t) { }
static void Apply1<T>(Action<T> f, T x)
{
f(x);
}
static void ApplyWrap1<T>(Action<T> f, T x)
{
Apply1(f, x);
}
void TestLambdaDispatch1()
{
ApplyWrap1(x => { Sink(x); }, Source("A")); // $ hasValueFlow=A
ApplyWrap1(x => { Sink(x); }, "B"); // no flow
ApplyWrap1(x => { }, Source("C"));
ApplyWrap1(x => { }, "D");
}
void ListForEachWrap<T>(List<T> l, Action<T> f)
{
l.ForEach(f);
}
void TestLambdaDispatch2()
{
var tainted = new List<string> { Source("E") };
var safe = new List<string>();
ListForEachWrap(safe, x => { Sink(x); }); // no flow
ListForEachWrap(tainted, x => { Sink(x); }); // $ hasValueFlow=E
}
static void Apply2<T>(Action<T> f, T x)
{
f(x);
}
static void ApplyWrap2<T>(Action<T> f, T x)
{
Apply2(f, x);
}
void SinkMethodGroup1<T>(T t) => Sink(t); // $ hasValueFlow=F $ hasValueFlow=G
void SinkMethodGroup2<T>(T t) => Sink(t);
void TestLambdaDispatch3()
{
ApplyWrap2(SinkMethodGroup1, Source("F"));
ApplyWrap2(SinkMethodGroup2, "B");
}
void ForEach<T>(List<T> l, Action<T> f)
{
foreach (var x in l)
f(x);
}
void ForEachWrap<T>(List<T> l, Action<T> f)
{
ForEach(l, f);
}
void TestLambdaDispatch4()
{
var tainted = new List<string> { Source("G") };
var safe = new List<string>();
ForEachWrap(safe, SinkMethodGroup2);
ForEachWrap(tainted, SinkMethodGroup1);
}
class TaintedClass
{
public override string ToString() => Source("TaintedClass");
}
class SafeClass
{
public override string ToString() => "safe";
}
string ConvertToString(object o) => o.ToString();
string ConvertToStringWrap(object o) => ConvertToString(o);
void TestToString1()
{
var unused = ConvertToStringWrap(new TaintedClass());
Sink(ConvertToStringWrap(new SafeClass())); // no flow
}
}

View File

@@ -0,0 +1,127 @@
testFailures
edges
| TypeFlowDispatch.cs:11:42:11:42 | x : String | TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String |
| TypeFlowDispatch.cs:11:42:11:42 | x : String | TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String |
| TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String | TypeFlowDispatch.cs:23:20:23:20 | x : String |
| TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String | TypeFlowDispatch.cs:23:20:23:20 | x : String |
| TypeFlowDispatch.cs:16:46:16:46 | x : String | TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String |
| TypeFlowDispatch.cs:16:46:16:46 | x : String | TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String |
| TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String | TypeFlowDispatch.cs:11:42:11:42 | x : String |
| TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String | TypeFlowDispatch.cs:11:42:11:42 | x : String |
| TypeFlowDispatch.cs:23:20:23:20 | x : String | TypeFlowDispatch.cs:23:32:23:32 | access to parameter x |
| TypeFlowDispatch.cs:23:20:23:20 | x : String | TypeFlowDispatch.cs:23:32:23:32 | access to parameter x |
| TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | TypeFlowDispatch.cs:16:46:16:46 | x : String |
| TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | TypeFlowDispatch.cs:16:46:16:46 | x : String |
| TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String | TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String | TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:39:34:39:34 | x : String |
| TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:39:34:39:34 | x : String |
| TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String | TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String | TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String | TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String |
| TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String | TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String |
| TypeFlowDispatch.cs:39:34:39:34 | x : String | TypeFlowDispatch.cs:39:46:39:46 | access to parameter x |
| TypeFlowDispatch.cs:39:34:39:34 | x : String | TypeFlowDispatch.cs:39:46:39:46 | access to parameter x |
| TypeFlowDispatch.cs:42:42:42:42 | x : String | TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String |
| TypeFlowDispatch.cs:42:42:42:42 | x : String | TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String |
| TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String | TypeFlowDispatch.cs:52:32:52:32 | t : String |
| TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String | TypeFlowDispatch.cs:52:32:52:32 | t : String |
| TypeFlowDispatch.cs:47:46:47:46 | x : String | TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String |
| TypeFlowDispatch.cs:47:46:47:46 | x : String | TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String |
| TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String | TypeFlowDispatch.cs:42:42:42:42 | x : String |
| TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String | TypeFlowDispatch.cs:42:42:42:42 | x : String |
| TypeFlowDispatch.cs:52:32:52:32 | t : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t |
| TypeFlowDispatch.cs:52:32:52:32 | t : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t |
| TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | TypeFlowDispatch.cs:47:46:47:46 | x : String |
| TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | TypeFlowDispatch.cs:47:46:47:46 | x : String |
| TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String | TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String | TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String | TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String |
| TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String | TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String |
| TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String |
| TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String |
| TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String | TypeFlowDispatch.cs:52:32:52:32 | t : String |
| TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String | TypeFlowDispatch.cs:52:32:52:32 | t : String |
| TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String | TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String | TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String |
| TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String | TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String |
| TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String | TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String | TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String | TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String |
| TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String | TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String |
nodes
| TypeFlowDispatch.cs:11:42:11:42 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:11:42:11:42 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:13:11:13:11 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:16:46:16:46 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:16:46:16:46 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:18:19:18:19 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:23:20:23:20 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:23:20:23:20 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | semmle.label | access to parameter x |
| TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | semmle.label | access to parameter x |
| TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:29:37:29:37 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:31:9:31:9 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String | semmle.label | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:36:23:36:54 | object creation of type List<String> : List<T> [element] : String | semmle.label | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String | semmle.label | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:39:25:39:31 | access to local variable tainted : List<T> [element] : String | semmle.label | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:39:34:39:34 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:39:34:39:34 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | semmle.label | access to parameter x |
| TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | semmle.label | access to parameter x |
| TypeFlowDispatch.cs:42:42:42:42 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:42:42:42:42 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:44:11:44:11 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:47:46:47:46 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:47:46:47:46 | x : String | semmle.label | x : String |
| TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:49:19:49:19 | access to parameter x : String | semmle.label | access to parameter x : String |
| TypeFlowDispatch.cs:52:32:52:32 | t : String | semmle.label | t : String |
| TypeFlowDispatch.cs:52:32:52:32 | t : String | semmle.label | t : String |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | semmle.label | access to parameter t |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | semmle.label | access to parameter t |
| TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:61:29:61:29 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String | semmle.label | SSA def(x) : String |
| TypeFlowDispatch.cs:63:22:63:22 | SSA def(x) : String | semmle.label | SSA def(x) : String |
| TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:63:27:63:27 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String | semmle.label | access to local variable x : String |
| TypeFlowDispatch.cs:64:15:64:15 | access to local variable x : String | semmle.label | access to local variable x : String |
| TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:67:33:67:33 | l : List<T> [element] : String | semmle.label | l : List<T> [element] : String |
| TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:69:17:69:17 | access to parameter l : List<T> [element] : String | semmle.label | access to parameter l : List<T> [element] : String |
| TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String | semmle.label | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:74:23:74:54 | object creation of type List<String> : List<T> [element] : String | semmle.label | object creation of type List<String> : List<T> [element] : String |
| TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | semmle.label | call to method Source<String> : String |
| TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String | semmle.label | access to local variable tainted : List<T> [element] : String |
| TypeFlowDispatch.cs:77:21:77:27 | access to local variable tainted : List<T> [element] : String | semmle.label | access to local variable tainted : List<T> [element] : String |
subpaths
#select
| TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | $@ | TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | TypeFlowDispatch.cs:23:32:23:32 | access to parameter x | $@ | TypeFlowDispatch.cs:23:39:23:49 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | $@ | TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | TypeFlowDispatch.cs:39:46:39:46 | access to parameter x | $@ | TypeFlowDispatch.cs:36:42:36:52 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | $@ | TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | $@ | TypeFlowDispatch.cs:57:38:57:48 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | $@ | TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | call to method Source<String> : String |
| TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | TypeFlowDispatch.cs:52:43:52:43 | access to parameter t | $@ | TypeFlowDispatch.cs:74:42:74:52 | call to method Source<String> : String | call to method Source<String> : String |

View File

@@ -0,0 +1,12 @@
/**
* @kind path-problem
*/
import csharp
import TestUtilities.InlineFlowTest
import DefaultFlowTest
import PathGraph
from PathNode source, PathNode sink
where flowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -0,0 +1,2 @@
semmle-extractor-options: /nostdlib /noconfig
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj