Merge pull request #5416 from hvitved/csharp/rework-summaries

C#: Rework flow summary implementation
This commit is contained in:
Tom Hvitved
2021-03-26 09:47:15 +01:00
committed by GitHub
32 changed files with 5600 additions and 5591 deletions

View File

@@ -1,5 +1,5 @@
/**
* Provides a language-independant implementation of static single assignment
* Provides a language-independent implementation of static single assignment
* (SSA) form.
*/

View File

@@ -1,5 +1,5 @@
/**
* Provides a language-independant implementation of static single assignment
* Provides a language-independent implementation of static single assignment
* (SSA) form.
*/

View File

@@ -1,181 +1,109 @@
/**
* Provides classes and predicates for definining flow summaries.
*/
/** Provides classes and predicates for defining flow summaries. */
import csharp
private import internal.FlowSummaryImpl as Impl
private import internal.FlowSummarySpecific::Private
private import internal.DataFlowPublic as DataFlowPublic
private import internal.DataFlowDispatch
// import all instances below
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
private import semmle.code.csharp.frameworks.EntityFramework
private module Summaries {
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
private import semmle.code.csharp.frameworks.EntityFramework
}
class SummarizableCallable = Impl::Public::SummarizableCallable;
class SummaryComponent = Impl::Public::SummaryComponent;
/** An unbound method. */
class SummarizableMethod extends SummarizableCallable, Method { }
/** Provides predicates for constructing summary components. */
module SummaryComponent {
import Impl::Public::SummaryComponent
class ContentList = Impl::Public::ContentList;
/** Gets a summary component that represents a qualifier. */
SummaryComponent qualifier() { result = argument(-1) }
/** Provides predicates for constructing content lists. */
module ContentList {
import Impl::Public::ContentList
/** Gets a summary component that represents an element in a collection. */
SummaryComponent element() { result = content(any(DataFlow::ElementContent c)) }
/** Gets the singleton "element content" content list. */
ContentList element() { result = singleton(any(DataFlowPublic::ElementContent c)) }
/** Gets a singleton property content list. */
ContentList property(Property p) {
result =
singleton(any(DataFlowPublic::PropertyContent c | c.getProperty() = p.getUnboundDeclaration()))
/** Gets a summary component for property `p`. */
SummaryComponent property(Property p) {
result = content(any(DataFlow::PropertyContent c | c.getProperty() = p.getUnboundDeclaration()))
}
/** Gets a singleton field content list. */
ContentList field(Field f) {
/** Gets a summary component for field `f`. */
SummaryComponent field(Field f) {
result = content(any(DataFlow::FieldContent c | c.getField() = f.getUnboundDeclaration()))
}
/** Gets a summary component that represents the return value of a call. */
SummaryComponent return() { result = return(any(NormalReturnKind rk)) }
/**
* Gets a summary component that represents the return value through the `i`th
* `out` argument of a call.
*/
SummaryComponent outArgument(int i) {
result = return(any(OutReturnKind rk | rk.getPosition() = i))
}
/**
* Gets a summary component that represents the return value through the `i`th
* `ref` argument of a call.
*/
SummaryComponent refArgument(int i) {
result = return(any(RefReturnKind rk | rk.getPosition() = i))
}
/** Gets a summary component that represents a jump to `c`. */
SummaryComponent jump(Callable c) {
result =
singleton(any(DataFlowPublic::FieldContent c | c.getField() = f.getUnboundDeclaration()))
return(any(JumpReturnKind jrk |
jrk.getTarget() = c.getUnboundDeclaration() and
jrk.getTargetReturnKind() instanceof NormalReturnKind
))
}
}
class SummaryInput = Impl::Public::SummaryInput;
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
/** Provides predicates for constructing flow-summary input specifications */
module SummaryInput {
private import semmle.code.csharp.frameworks.system.Collections
/** Provides predicates for constructing stacks of summary components. */
module SummaryComponentStack {
import Impl::Public::SummaryComponentStack
/**
* Gets an input specification that specifies the `i`th parameter as
* the input.
*/
SummaryInput parameter(int i) { result = TParameterSummaryInput(i) }
/** Gets a singleton stack representing a qualifier. */
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
private predicate isCollectionType(ValueOrRefType t) {
t.getABaseType*() instanceof SystemCollectionsIEnumerableInterface and
not t instanceof StringType
/** Gets a stack representing an element of `container`. */
SummaryComponentStack elementOf(SummaryComponentStack container) {
result = push(SummaryComponent::element(), container)
}
/**
* Gets an input specification that specifies the `i`th parameter as
* the input.
*
* `inputContents` is either empty or a singleton element content list,
* depending on whether the type of the `i`th parameter of `c` is a
* collection type.
*/
SummaryInput parameter(SummarizableCallable c, int i, ContentList inputContents) {
result = parameter(i) and
exists(Parameter p |
p = c.getParameter(i) and
if isCollectionType(p.getType())
then inputContents = ContentList::element()
else inputContents = ContentList::empty()
)
/** Gets a stack representing a propery `p` of `object`. */
SummaryComponentStack propertyOf(Property p, SummaryComponentStack object) {
result = push(SummaryComponent::property(p), object)
}
/**
* Gets an input specification that specifies the implicit `this` parameter
* as the input.
*/
SummaryInput thisParameter() { result = TParameterSummaryInput(-1) }
/**
* Gets an input specification that specifies output from the delegate at
* parameter `i` as the input.
*/
SummaryInput delegate(int i) { result = TDelegateSummaryInput(i) }
/**
* Gets an input specification that specifies output from the delegate at
* parameter `i` as the input.
*
* `c` must be a compatible callable, that is, a callable where the `i`th
* parameter is a delegate.
*/
SummaryInput delegate(SummarizableCallable c, int i) {
result = delegate(i) and
hasDelegateArgumentPosition(c, i)
}
}
class SummaryOutput = Impl::Public::SummaryOutput;
/** Provides predicates for constructing flow-summary output specifications. */
module SummaryOutput {
/**
* Gets an output specification that specifies the return value from a call as
* the output.
*/
SummaryOutput return() { result = TReturnSummaryOutput() }
/**
* Gets an output specification that specifies the `i`th parameter as the
* output.
*/
SummaryOutput parameter(int i) { result = TParameterSummaryOutput(i) }
/**
* Gets an output specification that specifies the implicit `this` parameter
* as the output.
*/
SummaryOutput thisParameter() { result = TParameterSummaryOutput(-1) }
/**
* Gets an output specification that specifies parameter `j` of the delegate at
* parameter `i` as the output.
*/
SummaryOutput delegate(int i, int j) { result = TDelegateSummaryOutput(i, j) }
/**
* Gets an output specification that specifies parameter `j` of the delegate at
* parameter `i` as the output.
*
* `c` must be a compatible callable, that is, a callable where the `i`th
* parameter is a delegate with a parameter at position `j`.
*/
SummaryOutput delegate(SummarizableCallable c, int i, int j) {
result = TDelegateSummaryOutput(i, j) and
hasDelegateArgumentPosition2(c, i, j)
/** Gets a stack representing a field `f` of `object`. */
SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) {
result = push(SummaryComponent::field(f), object)
}
/** Gets a singleton stack representing the return value of a call. */
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
/**
* Gets an output specification that specifies the `output` of `target` as the
* output. That is, data will flow into one callable and out of another callable
* (`target`).
*
* `output` is limited to (this) parameters and ordinary returns.
* Gets a singleton stack representing the return value through the `i`th
* `out` argument of a call.
*/
SummaryOutput jump(SummarizableCallable target, SummaryOutput output) {
result = TJumpSummaryOutput(target, toReturnKind(output))
}
SummaryComponentStack outArgument(int i) { result = singleton(SummaryComponent::outArgument(i)) }
/**
* Gets a singleton stack representing the return value through the `i`th
* `ref` argument of a call.
*/
SummaryComponentStack refArgument(int i) { result = singleton(SummaryComponent::refArgument(i)) }
/** Gets a singleton stack representing a jump to `c`. */
SummaryComponentStack jump(Callable c) { result = singleton(SummaryComponent::jump(c)) }
}
class SummarizedCallable = Impl::Public::SummarizedCallable;
/** Provides a query predicate for outputting a set of relevant flow summaries. */
module TestOutput {
/** A flow summary to include in the `summary/3` query predicate. */
abstract class RelevantSummarizedCallable extends SummarizedCallable { }
/** A query predicate for outputting flow summaries in QL tests. */
query predicate summary(string callable, string flow, boolean preservesValue) {
exists(
RelevantSummarizedCallable c, SummaryInput input, ContentList inputContents,
string inputContentsString, SummaryOutput output, ContentList outputContents,
string outputContentsString
|
callable = c.getQualifiedNameWithTypes() and
Impl::Private::summary(c, input, inputContents, output, outputContents, preservesValue) and
(
if inputContents.length() = 0
then inputContentsString = ""
else inputContentsString = " [" + inputContents + "]"
) and
(
if outputContents.length() = 0
then outputContentsString = ""
else outputContentsString = " [" + outputContents + "]"
) and
flow = input + inputContentsString + " -> " + output + outputContentsString
)
}
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

View File

@@ -354,86 +354,148 @@ abstract class LibraryTypeDataFlow extends Type {
}
}
private CallableFlowSource toCallableFlowSource(SummaryInput input) {
result = TCallableFlowSourceQualifier() and
input = SummaryInput::parameter(-1)
or
exists(int i |
result = TCallableFlowSourceArg(i) and
input = SummaryInput::parameter(i)
)
or
exists(int i |
result = TCallableFlowSourceDelegateArg(i) and
input = SummaryInput::delegate(i)
)
}
private CallableFlowSink toCallableFlowSink(SummaryOutput output) {
result = TCallableFlowSinkQualifier() and
output = SummaryOutput::parameter(-1)
or
result = TCallableFlowSinkReturn() and
output = SummaryOutput::return()
or
exists(int i |
result = TCallableFlowSinkArg(i) and
output = SummaryOutput::parameter(i)
)
or
exists(int i, int j |
result = TCallableFlowSinkDelegateArg(i, j) and
output = SummaryOutput::delegate(i, j)
)
}
private AccessPath toAccessPath(ContentList cl) {
cl = ContentList::empty() and
result = TNilAccessPath()
or
exists(Content head, ContentList tail |
cl = ContentList::cons(head, tail) and
result = TConsAccessPath(head, toAccessPath(tail))
)
}
private class FrameworkDataFlowAdaptor extends SummarizedCallable {
private LibraryTypeDataFlow ltdf;
FrameworkDataFlowAdaptor() {
ltdf.callableFlow(_, _, this, _) or
ltdf.callableFlow(_, _, _, _, this, _) or
ltdf.clearsContent(_, _, this)
}
override predicate propagatesFlow(SummaryInput input, SummaryOutput output, boolean preservesValue) {
ltdf.callableFlow(toCallableFlowSource(input), toCallableFlowSink(output), this, preservesValue)
}
override predicate propagatesFlow(
SummaryInput input, ContentList inputContents, SummaryOutput output, ContentList outputContents,
boolean preservesValue
) {
ltdf.callableFlow(toCallableFlowSource(input), toAccessPath(inputContents),
toCallableFlowSink(output), toAccessPath(outputContents), this, preservesValue)
}
private AccessPath getAnAccessPath() {
ltdf.callableFlow(_, result, _, _, this, _)
/**
* An internal module for translating old `LibraryTypeDataFlow`-style
* flow summaries into the new style.
*/
private module FrameworkDataFlowAdaptor {
private CallableFlowSource toCallableFlowSource(SummaryComponentStack input) {
result = TCallableFlowSourceQualifier() and
input = SummaryComponentStack::qualifier()
or
ltdf.callableFlow(_, _, _, result, _, _)
}
override predicate requiresContentList(Content head, ContentList tail) {
exists(AccessPath ap |
ap = this.getAnAccessPath().drop(_) and
head = ap.getHead() and
toAccessPath(tail) = ap.getTail()
exists(int i |
result = TCallableFlowSourceArg(i) and
input = SummaryComponentStack::argument(i)
)
or
exists(int i | result = TCallableFlowSourceDelegateArg(i) |
input =
SummaryComponentStack::push(SummaryComponent::return(), SummaryComponentStack::argument(i))
)
}
override predicate clearsContent(SummaryInput input, Content content) {
ltdf.clearsContent(toCallableFlowSource(input), content, this)
private CallableFlowSink toCallableFlowSink(SummaryComponentStack output) {
result = TCallableFlowSinkQualifier() and
output = SummaryComponentStack::qualifier()
or
result = TCallableFlowSinkReturn() and
output = SummaryComponentStack::return()
or
exists(int i |
result = TCallableFlowSinkArg(i) and
output = SummaryComponentStack::outArgument(i)
)
or
exists(int i, int j | result = TCallableFlowSinkDelegateArg(i, j) |
output =
SummaryComponentStack::push(SummaryComponent::parameter(j),
SummaryComponentStack::argument(i))
)
}
private class FrameworkDataFlowAdaptor extends SummarizedCallable {
private LibraryTypeDataFlow ltdf;
FrameworkDataFlowAdaptor() {
ltdf.callableFlow(_, _, this, _) or
ltdf.callableFlow(_, _, _, _, this, _) or
ltdf.clearsContent(_, _, this)
}
predicate input(
CallableFlowSource source, AccessPath sourceAp, SummaryComponent head,
SummaryComponentStack tail, int i
) {
ltdf.callableFlow(source, sourceAp, _, _, this, _) and
source = toCallableFlowSource(tail) and
head = SummaryComponent::content(sourceAp.getHead()) and
i = 0
or
exists(SummaryComponent tailHead, SummaryComponentStack tailTail |
this.input(source, sourceAp, tailHead, tailTail, i - 1) and
head = SummaryComponent::content(sourceAp.drop(i).getHead()) and
tail = SummaryComponentStack::push(tailHead, tailTail)
)
}
predicate output(
CallableFlowSink sink, AccessPath sinkAp, SummaryComponent head, SummaryComponentStack tail,
int i
) {
ltdf.callableFlow(_, _, sink, sinkAp, this, _) and
sink = toCallableFlowSink(tail) and
head = SummaryComponent::content(sinkAp.getHead()) and
i = 0
or
exists(SummaryComponent tailHead, SummaryComponentStack tailTail |
this.output(sink, sinkAp, tailHead, tailTail, i - 1) and
head = SummaryComponent::content(sinkAp.drop(i).getHead()) and
tail = SummaryComponentStack::push(tailHead, tailTail)
)
}
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
ltdf.callableFlow(toCallableFlowSource(input), toCallableFlowSink(output), this,
preservesValue)
or
exists(
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp
|
ltdf.callableFlow(source, sourceAp, sink, sinkAp, this, preservesValue) and
(
exists(SummaryComponent head, SummaryComponentStack tail |
this.input(source, sourceAp, head, tail, sourceAp.length() - 1) and
input = SummaryComponentStack::push(head, tail)
)
or
sourceAp.length() = 0 and
source = toCallableFlowSource(input)
) and
(
exists(SummaryComponent head, SummaryComponentStack tail |
this.output(sink, sinkAp, head, tail, sinkAp.length() - 1) and
output = SummaryComponentStack::push(head, tail)
)
or
sinkAp.length() = 0 and
sink = toCallableFlowSink(output)
)
)
}
override predicate clearsContent(int i, Content content) {
exists(SummaryComponentStack input |
ltdf.clearsContent(toCallableFlowSource(input), content, this) and
input = SummaryComponentStack::singleton(SummaryComponent::argument(i))
)
}
}
private class AdaptorRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
private SummaryComponent head;
AdaptorRequiredSummaryComponentStack() {
exists(int i |
exists(TCallableFlowSourceDelegateArg(i)) and
head = SummaryComponent::return() and
this = SummaryComponentStack::singleton(SummaryComponent::argument(i))
)
or
exists(int i, int j | exists(TCallableFlowSinkDelegateArg(i, j)) |
head = SummaryComponent::parameter(j) and
this = SummaryComponentStack::singleton(SummaryComponent::argument(i))
)
or
exists(FrameworkDataFlowAdaptor adaptor |
adaptor.input(_, _, head, this, _)
or
adaptor.output(_, _, head, this, _)
)
}
override predicate required(SummaryComponent c) { c = head }
}
}
@@ -2417,20 +2479,38 @@ class StringValuesFlow extends LibraryTypeDataFlow, Struct {
}
}
private predicate recordConstructorFlow(Constructor c, int i, Property p) {
c = any(Record r).getAMember() and
exists(string name |
c.getParameter(i).getName() = name and
c.getDeclaringType().getAMember(name) = p
)
}
private class RecordConstructorFlowRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
private SummaryComponent head;
RecordConstructorFlowRequiredSummaryComponentStack() {
exists(Property p |
recordConstructorFlow(_, _, p) and
head = SummaryComponent::property(p) and
this = SummaryComponentStack::singleton(SummaryComponent::return())
)
}
override predicate required(SummaryComponent c) { c = head }
}
private class RecordConstructorFlow extends SummarizedCallable {
RecordConstructorFlow() { this = any(Record r).getAMember().(Constructor) }
RecordConstructorFlow() { recordConstructorFlow(this, _, _) }
override predicate propagatesFlow(
SummaryInput input, ContentList inputContents, SummaryOutput output, ContentList outputContents,
boolean preservesValue
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
exists(int i, Property p, string name |
this.getParameter(i).getName() = name and
this.getDeclaringType().getAMember(name) = p and
input = SummaryInput::parameter(i) and
inputContents = ContentList::empty() and
output = SummaryOutput::return() and
outputContents = ContentList::property(p) and
exists(int i, Property p |
recordConstructorFlow(this, i, p) and
input = SummaryComponentStack::argument(i) and
output = SummaryComponentStack::propertyOf(p, SummaryComponentStack::return()) and
preservesValue = true
)
}

View File

@@ -9,6 +9,12 @@ private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
private predicate summarizedCallable(DataFlowCallable c) {
c instanceof SummarizedCallable
or
FlowSummaryImpl::Private::summaryReturnNode(_, TJumpReturnKind(c, _))
}
/**
* Gets a source declaration of callable `c` that has a body or has
* a flow summary.
@@ -18,11 +24,8 @@ private import semmle.code.csharp.frameworks.system.collections.Generic
*/
DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
exists(DotNet::Callable unboundDecl | unboundDecl = c.getUnboundDeclaration() |
result = unboundDecl and
result instanceof SummarizedCallable
or
result = unboundDecl and
FlowSummaryImpl::Private::summary(_, _, _, SummaryOutput::jump(result, _), _, _)
summarizedCallable(unboundDecl) and
result = unboundDecl
or
result.hasBody() and
if unboundDecl.getFile().fromSource()
@@ -44,17 +47,6 @@ DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
)
}
/**
* Holds if callable `c` can return `e` as an `out`/`ref` value for parameter `p`.
*/
private predicate callableReturnsOutOrRef(Callable c, Parameter p, Expr e) {
exists(Ssa::ExplicitDefinition def |
def.getADefinition().getSource() = e and
def.isLiveOutRefParameterDefinition(p) and
p = c.getAParameter()
)
}
/**
* Holds if `cfn` corresponds to a call that can reach callable `c` using
* additional calls, and `c` is a callable that either reads or writes to
@@ -82,18 +74,22 @@ private module Cached {
cached
newtype TReturnKind =
TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or
TOutReturnKind(int i) {
exists(Parameter p | callableReturnsOutOrRef(_, p, _) and p.isOut() | i = p.getPosition())
} or
TRefReturnKind(int i) {
exists(Parameter p | callableReturnsOutOrRef(_, p, _) and p.isRef() | i = p.getPosition())
} or
TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or
TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or
TImplicitCapturedReturnKind(LocalScopeVariable v) {
exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) |
v = def.getSourceVariable().getAssignable()
)
} or
TQualifierReturnKind()
TJumpReturnKind(DataFlowCallable target, ReturnKind rk) {
rk instanceof NormalReturnKind and
(
target instanceof Constructor or
not target.getReturnType() instanceof VoidType
)
or
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
}
cached
newtype TDataFlowCall =
@@ -110,16 +106,8 @@ private module Cached {
// No need to include calls that are compiled from source
not call.getImplementation().getMethod().compiledFromSource()
} or
TSummaryDelegateCall(SummarizedCallable c, int pos) {
exists(SummaryInput input |
FlowSummaryImpl::Private::summary(c, input, _, _, _, _) and
input = SummaryInput::delegate(pos)
)
or
exists(SummaryOutput output |
FlowSummaryImpl::Private::summary(c, _, _, output, _, _) and
output = SummaryOutput::delegate(pos, _)
)
TSummaryCall(SummarizedCallable c, Node receiver) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
/** Gets a viable run-time target for the call `call`. */
@@ -175,7 +163,7 @@ abstract class ReturnKind extends TReturnKind {
* body, that is, a "normal" return.
*/
class NormalReturnKind extends ReturnKind, TNormalReturnKind {
override string toString() { result = "return" }
override string toString() { result = "normal" }
}
/** A value returned from a callable using an `out` or a `ref` parameter. */
@@ -186,16 +174,24 @@ abstract class OutRefReturnKind extends ReturnKind {
/** A value returned from a callable using an `out` parameter. */
class OutReturnKind extends OutRefReturnKind, TOutReturnKind {
override int getPosition() { this = TOutReturnKind(result) }
private int pos;
override string toString() { result = "out" }
OutReturnKind() { this = TOutReturnKind(pos) }
override int getPosition() { result = pos }
override string toString() { result = "out parameter " + pos }
}
/** A value returned from a callable using a `ref` parameter. */
class RefReturnKind extends OutRefReturnKind, TRefReturnKind {
override int getPosition() { this = TRefReturnKind(result) }
private int pos;
override string toString() { result = "ref" }
RefReturnKind() { this = TRefReturnKind(pos) }
override int getPosition() { result = pos }
override string toString() { result = "ref parameter " + pos }
}
/** A value implicitly returned from a callable using a captured variable. */
@@ -210,12 +206,30 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
override string toString() { result = "captured " + v }
}
/** A value returned through the qualifier of a call. */
class QualifierReturnKind extends ReturnKind, TQualifierReturnKind {
override string toString() { result = "qualifier" }
/**
* A value returned through the output of another callable.
*
* This is currently only used to model flow summaries where data may flow into
* one API entry point and out of another.
*/
class JumpReturnKind extends ReturnKind, TJumpReturnKind {
private DataFlowCallable target;
private ReturnKind rk;
JumpReturnKind() { this = TJumpReturnKind(target, rk) }
/** Gets the target of the jump. */
DataFlowCallable getTarget() { result = target }
/** Gets the return kind of the target. */
ReturnKind getTargetReturnKind() { result = rk }
override string toString() { result = "jump to " + target }
}
class DataFlowCallable = DotNet::Callable;
class DataFlowCallable extends DotNet::Callable {
DataFlowCallable() { this.isUnboundDeclaration() }
}
/** A call relevant for data flow. */
abstract class DataFlowCall extends TDataFlowCall {
@@ -266,7 +280,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = cfn.toString() }
@@ -294,7 +308,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = cfn.toString() }
@@ -312,13 +326,13 @@ class TransitiveCapturedDataFlowCall extends DataFlowCall, TTransitiveCapturedCa
TransitiveCapturedDataFlowCall() { this = TTransitiveCapturedCall(cfn, target) }
override Callable getARuntimeTarget() { result = target }
override DataFlowCallable getARuntimeTarget() { result = target }
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
override DataFlow::ExprNode getNode() { none() }
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = "[transitive] " + cfn.toString() }
@@ -340,7 +354,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
override DataFlow::ExprNode getNode() { result.getExpr() = call }
override CIL::Callable getEnclosingCallable() { result = call.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallable() { result = call.getEnclosingCallable() }
override string toString() { result = call.toString() }
@@ -348,20 +362,20 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
}
/**
* A delegate call inside a callable with a flow summary.
* A synthesized call inside a callable with a flow summary.
*
* For example, in `ints.Select(i => i + 1)` there is a call to the delegate at
* parameter position `1` (counting the qualifier as the `0`th argument) inside
* the method `Select`.
*/
class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall {
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
private SummarizedCallable c;
private int pos;
private Node receiver;
SummaryDelegateCall() { this = TSummaryDelegateCall(c, pos) }
SummaryCall() { this = TSummaryCall(c, receiver) }
/** Gets the parameter node that this delegate call targets. */
ParameterNode getParameterNode() { result.isParameterOf(c, pos) }
/** Gets the data flow node that this call targets. */
Node getReceiver() { result = receiver }
override DataFlowCallable getARuntimeTarget() {
none() // handled by the shared library
@@ -371,9 +385,9 @@ class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall {
override DataFlow::Node getNode() { none() }
override Callable getEnclosingCallable() { result = c }
override DataFlowCallable getEnclosingCallable() { result = c }
override string toString() { result = "[summary] delegate call, parameter " + pos + " of " + c }
override string toString() { result = "[summary] call to " + receiver + " in " + c }
override Location getLocation() { result = c.getLocation() }
}

View File

@@ -18,7 +18,6 @@ private import semmle.code.csharp.frameworks.EntityFramework
private import semmle.code.csharp.frameworks.NHibernate
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.threading.Tasks
private import semmle.code.csharp.frameworks.system.linq.Expressions
abstract class NodeImpl extends Node {
/** Do not call: use `getEnclosingCallable()` instead. */
@@ -70,9 +69,6 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
}
}
/** A data-flow node used to interpret a flow summary. */
abstract private class SummaryNodeImpl extends NodeImpl { }
/** Calculation of the relative order in which `this` references are read. */
private module ThisFlow {
private class BasicBlock = ControlFlow::BasicBlock;
@@ -379,7 +375,7 @@ module LocalFlow {
* inter-procedurality or field-sensitivity.
*/
predicate excludeFromExposedRelations(Node n) {
n instanceof SummaryNodeImpl or
n instanceof SummaryNode or
n instanceof ImplicitCapturedArgumentNode
}
}
@@ -455,9 +451,9 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
f.isFieldLike() and
f instanceof InstanceFieldOrProperty
or
exists(ContentList cl |
FlowSummaryImpl::Private::summary(_, _, cl, _, _, _) and
cl.contains(f.getContent())
exists(SummarizedCallable callable, FlowSummaryImpl::Public::SummaryComponentStack input |
callable.propagatesFlow(input, _, _) and
input.contains(SummaryComponent::content(f.getContent()))
)
)
|
@@ -611,8 +607,6 @@ private Gvn::GvnType getANonTypeParameterSubTypeRestricted(DataFlowType t) {
/** A collection of cached types and predicates to be evaluated in the same stage. */
cached
private module Cached {
private import FlowSummarySpecific as FlowSummarySpecific
cached
newtype TNode =
TExprNode(ControlFlow::Nodes::ElementNode cfn) {
@@ -664,32 +658,8 @@ private module Cached {
cfn.getElement() = fla.getQualifier()
)
} or
TSummaryInternalNode(
SummarizedCallable c, FlowSummaryImpl::Private::SummaryInternalNodeState state
) {
FlowSummaryImpl::Private::internalNodeRange(c, state)
} or
TSummaryReturnNode(SummarizedCallable c, ReturnKind rk) {
exists(SummaryOutput output |
FlowSummaryImpl::Private::summary(c, _, _, output, _, _) and
rk = FlowSummarySpecific::Private::toReturnKind(output)
)
} or
TSummaryDelegateOutNode(SummarizedCallable c, int pos) {
exists(SummaryInput input |
FlowSummaryImpl::Private::summary(c, input, _, _, _, _) and
input = SummaryInput::delegate(pos)
)
} or
TSummaryDelegateArgumentNode(SummarizedCallable c, int delegateIndex, int parameterIndex) {
exists(SummaryOutput output |
FlowSummaryImpl::Private::summary(c, _, _, output, _, _) and
output = SummaryOutput::delegate(delegateIndex, parameterIndex)
)
} or
TSummaryJumpNode(SummarizedCallable c, SummarizableCallable target, ReturnKind rk) {
FlowSummaryImpl::Private::summary(c, _, _,
FlowSummarySpecific::Private::TJumpSummaryOutput(target, rk), _, _)
TSummaryNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
FlowSummaryImpl::Private::summaryNodeRange(c, state)
} or
TParamsArgumentNode(ControlFlow::Node callCfn) {
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
@@ -707,7 +677,7 @@ private module Cached {
or
LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo)
or
FlowSummaryImpl::Private::localStep(nodeFrom, nodeTo, true)
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
or
nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode)
}
@@ -727,7 +697,7 @@ private module Cached {
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::throughStep(nodeFrom, nodeTo, true)
FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true)
}
/**
@@ -748,7 +718,11 @@ private module Cached {
flr.hasNonlocalValue()
)
or
succ = pred.(SummaryJumpNode).getAJumpTarget()
exists(JumpReturnKind jrk, DataFlowCall call |
FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and
viableCallable(call) = jrk.getTarget() and
succ = getAnOutNode(call, jrk.getTargetReturnKind())
)
}
cached
@@ -791,7 +765,7 @@ private module Cached {
c = getResultContent()
)
or
FlowSummaryImpl::Private::storeStep(node1, c, node2)
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
}
pragma[nomagic]
@@ -855,7 +829,7 @@ private module Cached {
)
)
or
FlowSummaryImpl::Private::readStep(node1, c, node2)
FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2)
}
/**
@@ -869,14 +843,10 @@ private module Cached {
or
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
or
FlowSummaryImpl::Private::storeStep(n, c, _) and
FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and
not c instanceof ElementContent
or
exists(SummaryInput input, DataFlowCall call, int i |
FlowSummaryImpl::Private::clearsContent(input, call, c) and
input = SummaryInput::parameter(i) and
n.(ArgumentNode).argumentOf(call, i)
)
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
or
exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f |
oi = we.getInitializer() and
@@ -938,11 +908,24 @@ private module Cached {
}
cached
predicate qualifierOutNode(DataFlowCall call, Node n) {
n.(ExprPostUpdateNode).getPreUpdateNode().(ExplicitArgumentNode).argumentOf(call, -1)
or
any(ObjectOrCollectionInitializerConfiguration x)
.hasExprPath(_, n.(ExprNode).getControlFlowNode(), _, call.getControlFlowNode())
predicate summaryOutNodeCached(DataFlowCall c, Node out, ReturnKind rk) {
FlowSummaryImpl::Private::summaryOutNode(c, out, rk)
}
cached
predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) {
FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i)
}
cached
predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) {
FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre)
}
cached
predicate summaryReturnNodeCached(Node ret, ReturnKind rk) {
FlowSummaryImpl::Private::summaryReturnNode(ret, rk) and
not rk instanceof JumpReturnKind
}
cached
@@ -968,6 +951,8 @@ private module Cached {
not p.fromSource()
)
or
n = TInstanceParameterNode(any(Callable c | not c.fromSource()))
or
n instanceof YieldReturnNode
or
n instanceof AsyncReturnNode
@@ -976,7 +961,7 @@ private module Cached {
or
n instanceof MallocNode
or
n instanceof SummaryNodeImpl
n instanceof SummaryNode
or
n instanceof ParamsArgumentNode
or
@@ -995,7 +980,7 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
/** Gets the underlying SSA definition. */
Ssa::Definition getDefinition() { result = def }
override Callable getEnclosingCallableImpl() { result = def.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = def.getEnclosingCallable() }
override Type getTypeImpl() { result = def.getSourceVariable().getType() }
@@ -1006,9 +991,13 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
override string toStringImpl() { result = def.toString() }
}
private module ParameterNodes {
abstract private class ParameterNodeImpl extends ParameterNode, NodeImpl { }
abstract class ParameterNodeImpl extends NodeImpl {
abstract DotNet::Parameter getParameter();
abstract predicate isParameterOf(DataFlowCallable c, int i);
}
private module ParameterNodes {
/**
* The value of an explicit parameter at function entry, viewed as a node in a data
* flow graph.
@@ -1048,9 +1037,11 @@ private module ParameterNodes {
/** Gets the callable containing this implicit instance parameter. */
Callable getCallable() { result = callable }
override DotNet::Parameter getParameter() { none() }
override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 }
override Callable getEnclosingCallableImpl() { result = callable }
override DataFlowCallable getEnclosingCallableImpl() { result = callable }
override Type getTypeImpl() { result = callable.getDeclaringType() }
@@ -1114,7 +1105,7 @@ private module ParameterNodes {
* } }
* ```
*/
class ImplicitCapturedParameterNode extends ParameterNode, SsaDefinitionNode {
class ImplicitCapturedParameterNode extends ParameterNodeImpl, SsaDefinitionNode {
override SsaCapturedEntryDefinition def;
ImplicitCapturedParameterNode() { def = this.getDefinition() }
@@ -1122,6 +1113,8 @@ private module ParameterNodes {
/** Gets the captured variable that this implicit parameter models. */
LocalScopeVariable getVariable() { result = def.getVariable() }
override DotNet::Parameter getParameter() { none() }
override predicate isParameterOf(DataFlowCallable c, int i) {
i = getParameterPosition(def) and
c = this.getEnclosingCallable()
@@ -1223,7 +1216,7 @@ private module ArgumentNodes {
)
}
override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override Type getTypeImpl() { result = v.getType() }
@@ -1250,7 +1243,7 @@ private module ArgumentNodes {
override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() }
@@ -1287,7 +1280,7 @@ private module ArgumentNodes {
pos = this.getParameter().getPosition()
}
override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() }
override Type getTypeImpl() { result = this.getParameter().getType() }
@@ -1298,46 +1291,15 @@ private module ArgumentNodes {
override string toStringImpl() { result = "[implicit array creation] " + callCfn }
}
/**
* An argument node inside a callable with a flow summary, where the argument is
* passed to a supplied delegate. For example, in `ints.Select(Foo)` there is a
* node that represents the argument of the call to `Foo` inside `Select`.
*/
class SummaryDelegateArgumentNode extends ArgumentNode, SummaryNodeImpl,
TSummaryDelegateArgumentNode {
private SummarizedCallable c;
private int delegateIndex;
private int parameterIndex;
private class SummaryArgumentNode extends SummaryNode, ArgumentNode {
private DataFlowCall c;
private int i;
SummaryDelegateArgumentNode() {
this = TSummaryDelegateArgumentNode(c, delegateIndex, parameterIndex)
}
override DataFlowCallable getEnclosingCallableImpl() { result = c }
override DotNet::Type getTypeImpl() {
result =
c.getParameter(delegateIndex)
.getType()
.(SystemLinqExpressions::DelegateExtType)
.getDelegateType()
.getParameter(parameterIndex)
.getType()
}
override ControlFlow::Node getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = c.getLocation() }
override string toStringImpl() {
result =
"[summary] argument " + parameterIndex + " of delegate call, parameter " + parameterIndex +
" of " + c
}
SummaryArgumentNode() { summaryArgumentNodeCached(c, this, i) }
override predicate argumentOf(DataFlowCall call, int pos) {
call = TSummaryDelegateCall(c, delegateIndex) and
pos = parameterIndex
call = c and
i = pos
}
}
}
@@ -1362,8 +1324,9 @@ private module ReturnNodes {
)
}
override ReturnKind getKind() {
any(DotNet::Callable c).canReturn(this.getExpr()) and result instanceof NormalReturnKind
override NormalReturnKind getKind() {
any(DotNet::Callable c).canReturn(this.getExpr()) and
exists(result)
}
}
@@ -1394,7 +1357,7 @@ private module ReturnNodes {
override NormalReturnKind getKind() { any() }
override Callable getEnclosingCallableImpl() { result = yrs.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = yrs.getEnclosingCallable() }
override Type getTypeImpl() { result = yrs.getEnclosingCallable().getReturnType() }
@@ -1418,7 +1381,7 @@ private module ReturnNodes {
override NormalReturnKind getKind() { any() }
override Callable getEnclosingCallableImpl() { result = expr.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = expr.getEnclosingCallable() }
override Type getTypeImpl() { result = expr.getEnclosingCallable().getReturnType() }
@@ -1468,30 +1431,10 @@ private module ReturnNodes {
}
}
/** A return node for a callable with a flow summary. */
class SummaryReturnNode extends ReturnNode, SummaryNodeImpl, TSummaryReturnNode {
private SummarizedCallable sc;
private class SummaryReturnNode extends SummaryNode, ReturnNode {
private ReturnKind rk;
SummaryReturnNode() { this = TSummaryReturnNode(sc, rk) }
override Callable getEnclosingCallableImpl() { result = sc }
override DotNet::Type getTypeImpl() {
rk instanceof NormalReturnKind and
result in [sc.getReturnType(), sc.(Constructor).getDeclaringType()]
or
rk instanceof QualifierReturnKind and
result = sc.getDeclaringType()
or
result = sc.getParameter(rk.(OutRefReturnKind).getPosition()).getType()
}
override ControlFlow::Node getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = sc.getLocation() }
override string toStringImpl() { result = "[summary] return of kind " + rk + " inside " + sc }
SummaryReturnNode() { summaryReturnNodeCached(this, rk) }
override ReturnKind getKind() { result = rk }
}
@@ -1549,32 +1492,17 @@ private module OutNodes {
) {
exactScope = false and
scope = e1 and
isSuccessor = false and
(
isSuccessor = true and
exists(ObjectOrCollectionInitializer init | init = e1.(ObjectCreation).getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
e1.(CollectionInitializer).getAnElementInitializer() = e2
e2 = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
e1.(ObjectInitializer).getAMemberInitializer().getLValue() = e2
e2 = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
}
/**
* A data-flow node that contains a value returned by a callable, by writing
* to the qualifier of the call.
*/
private class QualifierOutNode extends OutNode, Node {
private DataFlowCall call;
QualifierOutNode() { qualifierOutNode(call, this) }
override DataFlowCall getCall(ReturnKind kind) {
result = call and
kind instanceof QualifierReturnKind
}
}
/**
* A data-flow node that reads a value returned implicitly by a callable
* using a captured variable.
@@ -1620,39 +1548,15 @@ private module OutNodes {
}
}
/**
* An output node inside a callable with a flow summary, where the output is the
* result of calling a supplied delegate. For example, in `ints.Select(Foo)` there
* is a node that represents the output of calling `Foo` inside `Select`.
*/
private class SummaryDelegateOutNode extends OutNode, SummaryNodeImpl, TSummaryDelegateOutNode {
private SummarizedCallable c;
private int pos;
private class SummaryOutNode extends SummaryNode, OutNode {
private DataFlowCall c;
private ReturnKind rk;
SummaryDelegateOutNode() { this = TSummaryDelegateOutNode(c, pos) }
SummaryOutNode() { summaryOutNodeCached(c, this, rk) }
override Callable getEnclosingCallableImpl() { result = c }
override DotNet::Type getTypeImpl() {
result =
c.getParameter(pos)
.getType()
.(SystemLinqExpressions::DelegateExtType)
.getDelegateType()
.getReturnType()
}
override ControlFlow::Node getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = c.getLocation() }
override string toStringImpl() {
result = "[summary] output from delegate call, parameter " + pos + " of " + c + "]"
}
override SummaryDelegateCall getCall(ReturnKind kind) {
result = TSummaryDelegateCall(c, pos) and
kind instanceof NormalReturnKind
override DataFlowCall getCall(ReturnKind kind) {
result = c and
kind = rk
}
}
}
@@ -1660,15 +1564,17 @@ private module OutNodes {
import OutNodes
/** A data-flow node used to model flow summaries. */
private class SummaryInternalNode extends SummaryNodeImpl, TSummaryInternalNode {
private class SummaryNode extends NodeImpl, TSummaryNode {
private SummarizedCallable c;
private FlowSummaryImpl::Private::SummaryInternalNodeState state;
private FlowSummaryImpl::Private::SummaryNodeState state;
SummaryInternalNode() { this = TSummaryInternalNode(c, state) }
SummaryNode() { this = TSummaryNode(c, state) }
override DataFlowCallable getEnclosingCallableImpl() { result = c }
override DataFlowType getDataFlowType() { result = state.getType() }
override DataFlowType getDataFlowType() {
result = FlowSummaryImpl::Private::summaryNodeType(this)
}
override DotNet::Type getTypeImpl() { none() }
@@ -1679,28 +1585,6 @@ private class SummaryInternalNode extends SummaryNodeImpl, TSummaryInternalNode
override string toStringImpl() { result = "[summary] " + state + " in " + c }
}
/** A data-flow node used to model flow summaries with jumps. */
private class SummaryJumpNode extends SummaryNodeImpl, TSummaryJumpNode {
private SummarizedCallable c;
private SummarizableCallable target;
private ReturnKind rk;
SummaryJumpNode() { this = TSummaryJumpNode(c, target, rk) }
/** Gets a jump target of this node. */
OutNode getAJumpTarget() { target = viableCallable(result.getCall(rk)) }
override Callable getEnclosingCallableImpl() { result = c }
override DotNet::Type getTypeImpl() { result = target.getReturnType() }
override ControlFlow::Node getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = c.getLocation() }
override string toStringImpl() { result = "[summary] jump to " + target }
}
/** A field or a property. */
class FieldOrProperty extends Assignable, Modifiable {
FieldOrProperty() {
@@ -1935,7 +1819,7 @@ private module PostUpdateNodes {
* Such a node acts as both a post-update node for the `MallocNode`, as well as
* a pre-update node for the `ObjectCreationNode`.
*/
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, TObjectInitializerNode {
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNode, TObjectInitializerNode {
private ObjectCreation oc;
private ControlFlow::Nodes::ElementNode cfn;
@@ -1949,7 +1833,13 @@ private module PostUpdateNodes {
override MallocNode getPreUpdateNode() { result.getControlFlowNode() = cfn }
override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override predicate argumentOf(DataFlowCall call, int pos) {
pos = -1 and
any(ObjectOrCollectionInitializerConfiguration x)
.hasExprPath(_, cfn, _, call.getControlFlowNode())
}
override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override DotNet::Type getTypeImpl() { result = oc.getType() }
@@ -1967,7 +1857,7 @@ private module PostUpdateNodes {
override ExprNode getPreUpdateNode() { cfn = result.getControlFlowNode() }
override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override DataFlowCallable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() }
override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() }
@@ -1977,6 +1867,14 @@ private module PostUpdateNodes {
override string toStringImpl() { result = "[post] " + cfn.toString() }
}
private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode {
private Node pre;
SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) }
override Node getPreUpdateNode() { result = pre }
}
}
private import PostUpdateNodes
@@ -2077,7 +1975,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
call.getControlFlowNode())
)
or
receiver = call.(SummaryDelegateCall).getParameterNode()
receiver = call.(SummaryCall).getReceiver()
) and
kind = TMkUnit()
}

View File

@@ -117,22 +117,18 @@ class ExprNode extends Node {
* flow graph.
*/
class ParameterNode extends Node {
ParameterNode() {
// charpred needed to avoid making `ParameterNode` abstract
this = TExplicitParameterNode(_) or
this.(SsaDefinitionNode).getDefinition() instanceof
ImplicitCapturedParameterNodeImpl::SsaCapturedEntryDefinition or
this = TInstanceParameterNode(_)
}
private ParameterNodeImpl p;
ParameterNode() { this = p }
/** Gets the parameter corresponding to this node, if any. */
DotNet::Parameter getParameter() { none() }
DotNet::Parameter getParameter() { result = p.getParameter() }
/**
* Holds if this node is the parameter of callable `c` at the specified
* (zero-based) position.
*/
predicate isParameterOf(DataFlowCallable c, int i) { none() }
predicate isParameterOf(DataFlowCallable c, int i) { p.isParameterOf(c, i) }
}
/** A definition, viewed as a node in a data flow graph. */
@@ -236,7 +232,7 @@ class FieldContent extends Content, TFieldContent {
/** Gets the field that is referenced. */
Field getField() { result = f }
override string toString() { result = f.toString() }
override string toString() { result = "field " + f.getName() }
override Location getLocation() { result = f.getLocation() }
@@ -256,7 +252,7 @@ class PropertyContent extends Content, TPropertyContent {
/** Gets the property that is referenced. */
Property getProperty() { result = p }
override string toString() { result = p.toString() }
override string toString() { result = "property " + p.getName() }
override Location getLocation() { result = p.getLocation() }
@@ -269,7 +265,7 @@ class PropertyContent extends Content, TPropertyContent {
/** A reference to an element in a collection. */
class ElementContent extends Content, TElementContent {
override string toString() { result = "[]" }
override string toString() { result = "element" }
override Location getLocation() { result instanceof EmptyLocation }
}

View File

@@ -0,0 +1,79 @@
/**
* Provides C# specific classes and predicates for defining flow summaries.
*/
private import csharp
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import DataFlowDispatch
private import DataFlowPrivate
private import DataFlowPublic
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import semmle.code.csharp.Unification
/** Holds is `i` is a valid parameter position. */
predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] }
/** Gets the synthesized summary data-flow node for the given values. */
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) }
/** Gets the synthesized data-flow call for `receiver`. */
SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() }
/** Gets the type of content `c`. */
DataFlowType getContentType(Content c) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
t = c.(FieldContent).getField().getType()
or
t = c.(PropertyContent).getProperty().getType()
or
c instanceof ElementContent and
t instanceof ObjectType // we don't know what the actual element type is
)
}
private DataFlowType getReturnTypeBase(DataFlowCallable c, ReturnKind rk) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
rk instanceof NormalReturnKind and
(
t = c.(Constructor).getDeclaringType()
or
not c instanceof Constructor and
t = c.getReturnType()
)
or
t = c.getParameter(rk.(OutRefReturnKind).getPosition()).getType()
)
}
/** Gets the return type of kind `rk` for callable `c`. */
bindingset[c]
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) {
result = getReturnTypeBase(c, rk)
or
rk =
any(JumpReturnKind jrk | result = getReturnTypeBase(jrk.getTarget(), jrk.getTargetReturnKind()))
}
/**
* Gets the type of the `i`th parameter in a synthesized call that targets a
* callback of type `t`.
*/
DataFlowType getCallbackParameterType(DataFlowType t, int i) {
exists(SystemLinqExpressions::DelegateExtType dt |
t = Gvn::getGlobalValueNumber(dt) and
result = Gvn::getGlobalValueNumber(dt.getDelegateType().getParameter(i).getType())
)
}
/**
* Gets the return type of kind `rk` in a synthesized call that targets a
* callback of type `t`.
*/
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())
)
}

View File

@@ -1,178 +0,0 @@
/**
* Provides C# specific classes and predicates for definining flow summaries.
*/
private import csharp
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import DataFlowDispatch
module Private {
private import Public
private import DataFlowPrivate as DataFlowPrivate
private import DataFlowPublic as DataFlowPublic
private import FlowSummaryImpl as Impl
private import semmle.code.csharp.Unification
class Content = DataFlowPublic::Content;
class DataFlowType = DataFlowPrivate::DataFlowType;
class Node = DataFlowPublic::Node;
class ParameterNode = DataFlowPublic::ParameterNode;
class ArgumentNode = DataFlowPrivate::ArgumentNode;
class ReturnNode = DataFlowPrivate::ReturnNode;
class OutNode = DataFlowPrivate::OutNode;
private class NodeImpl = DataFlowPrivate::NodeImpl;
predicate accessPathLimit = DataFlowPrivate::accessPathLimit/0;
predicate hasDelegateArgumentPosition(SummarizableCallable c, int i) {
exists(DelegateType dt |
dt = c.getParameter(i).getType().(SystemLinqExpressions::DelegateExtType).getDelegateType()
|
not dt.getReturnType() instanceof VoidType
)
}
predicate hasDelegateArgumentPosition2(SummarizableCallable c, int i, int j) {
exists(DelegateType dt |
dt = c.getParameter(i).getType().(SystemLinqExpressions::DelegateExtType).getDelegateType()
|
exists(dt.getParameter(j))
)
}
newtype TSummaryInput =
TParameterSummaryInput(int i) { i in [-1, any(Parameter p).getPosition()] } or
TDelegateSummaryInput(int i) { hasDelegateArgumentPosition(_, i) }
newtype TSummaryOutput =
TReturnSummaryOutput() or
TParameterSummaryOutput(int i) {
i in [-1, any(SummarizableCallable c).getAParameter().getPosition()]
} or
TDelegateSummaryOutput(int i, int j) { hasDelegateArgumentPosition2(_, i, j) } or
TJumpSummaryOutput(SummarizableCallable target, ReturnKind rk) {
rk instanceof NormalReturnKind and
(
target instanceof Constructor or
not target.getReturnType() instanceof VoidType
)
or
rk instanceof QualifierReturnKind and
not target.(Modifiable).isStatic()
or
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
}
/** Gets the return kind that matches `sink`, if any. */
ReturnKind toReturnKind(SummaryOutput output) {
output = TReturnSummaryOutput() and
result instanceof NormalReturnKind
or
exists(int i | output = TParameterSummaryOutput(i) |
i = -1 and
result instanceof QualifierReturnKind
or
i = result.(OutRefReturnKind).getPosition()
)
}
/** Gets the input node for `c` of type `input`. */
NodeImpl inputNode(SummarizableCallable c, SummaryInput input) {
exists(int i |
input = TParameterSummaryInput(i) and
result.(ParameterNode).isParameterOf(c, i)
)
or
exists(int i |
input = TDelegateSummaryInput(i) and
result = DataFlowPrivate::TSummaryDelegateOutNode(c, i)
)
}
/** Gets the output node for `c` of type `output`. */
NodeImpl outputNode(SummarizableCallable c, SummaryOutput output) {
result = DataFlowPrivate::TSummaryReturnNode(c, toReturnKind(output))
or
exists(int i, int j |
output = TDelegateSummaryOutput(i, j) and
result = DataFlowPrivate::TSummaryDelegateArgumentNode(c, i, j)
)
or
exists(SummarizableCallable target, ReturnKind rk |
output = TJumpSummaryOutput(target, rk) and
result = DataFlowPrivate::TSummaryJumpNode(c, target, rk)
)
}
/** Gets the internal summary node for the given values. */
Node internalNode(SummarizableCallable c, Impl::Private::SummaryInternalNodeState state) {
result = DataFlowPrivate::TSummaryInternalNode(c, state)
}
/** Gets the type of content `c`. */
pragma[noinline]
DataFlowType getContentType(Content c) {
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
t = c.(DataFlowPublic::FieldContent).getField().getType()
or
t = c.(DataFlowPublic::PropertyContent).getProperty().getType()
or
c instanceof DataFlowPublic::ElementContent and
t instanceof ObjectType // we don't know what the actual element type is
)
}
}
module Public {
private import Private
/** An unbound callable. */
class SummarizableCallable extends Callable {
SummarizableCallable() { this.isUnboundDeclaration() }
}
/** A flow-summary input specification. */
class SummaryInput extends TSummaryInput {
/** Gets a textual representation of this input specification. */
final string toString() {
exists(int i |
this = TParameterSummaryInput(i) and
if i = -1 then result = "this parameter" else result = "parameter " + i
or
this = TDelegateSummaryInput(i) and
result = "deleget output from parameter " + i
)
}
}
/** A flow-summary output specification. */
class SummaryOutput extends TSummaryOutput {
/** Gets a textual representation of this flow sink specification. */
final string toString() {
this = TReturnSummaryOutput() and
result = "return"
or
exists(int i |
this = TParameterSummaryOutput(i) and
if i = -1 then result = "this parameter" else result = "parameter " + i
)
or
exists(int delegateIndex, int parameterIndex |
this = TDelegateSummaryOutput(delegateIndex, parameterIndex) and
result = "parameter " + parameterIndex + " of delegate parameter " + delegateIndex
)
or
exists(SummarizableCallable target, ReturnKind rk |
this = TJumpSummaryOutput(target, rk) and
result = "jump to " + target + " (" + rk + ")"
)
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Provides a language-independant implementation of static single assignment
* Provides a language-independent implementation of static single assignment
* (SSA) form.
*/

View File

@@ -103,19 +103,19 @@ private module Cached {
(
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::throughStep(nodeFrom, nodeTo, false)
FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, false)
or
// Taint collection by adding a tainted element
exists(DataFlow::ElementContent c |
storeStep(nodeFrom, c, nodeTo)
or
FlowSummaryImpl::Private::setterStep(nodeFrom, c, nodeTo)
FlowSummaryImpl::Private::Steps::summarySetterStep(nodeFrom, c, nodeTo)
)
or
exists(DataFlow::Content c |
readStep(nodeFrom, c, nodeTo)
or
FlowSummaryImpl::Private::getterStep(nodeFrom, c, nodeTo)
FlowSummaryImpl::Private::Steps::summaryGetterStep(nodeFrom, c, nodeTo)
|
// Taint members
c = any(TaintedMember m).(FieldOrProperty).getContent()
@@ -142,7 +142,7 @@ private module Cached {
// tracking configurations where the source is a collection
readStep(nodeFrom, TElementContent(), nodeTo)
or
FlowSummaryImpl::Private::localStep(nodeFrom, nodeTo, false)
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
or
nodeTo = nodeFrom.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}

View File

@@ -1,5 +1,5 @@
/**
* Provides a language-independant implementation of static single assignment
* Provides a language-independent implementation of static single assignment
* (SSA) form.
*/

View File

@@ -9,6 +9,7 @@ private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.system.collections.Generic
private import semmle.code.csharp.frameworks.Sql
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
/**
* Definitions relating to the `System.ComponentModel.DataAnnotations`
@@ -74,7 +75,7 @@ module EntityFramework {
DbSet() { this.getName() = "DbSet<>" }
/** Gets a method that adds or updates entities in a DB set. */
SummarizableMethod getAnAddOrUpdateMethod(boolean range) {
Method getAnAddOrUpdateMethod(boolean range) {
exists(string name | result = this.getAMethod(name) |
name in ["Add", "AddAsync", "Attach", "Update"] and
range = false
@@ -88,23 +89,31 @@ module EntityFramework {
/** A flow summary for EntityFramework. */
abstract class EFSummarizedCallable extends SummarizedCallable { }
private class DbSetAddOrUpdateRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
private SummaryComponent head;
DbSetAddOrUpdateRequiredSummaryComponentStack() {
this = SummaryComponentStack::argument([-1, 0]) and
head = SummaryComponent::element()
}
override predicate required(SummaryComponent c) { c = head }
}
private class DbSetAddOrUpdate extends EFSummarizedCallable {
private boolean range;
DbSetAddOrUpdate() { this = any(DbSet c).getAnAddOrUpdateMethod(range) }
override predicate propagatesFlow(
SummaryInput input, ContentList inputContents, SummaryOutput output,
ContentList outputContents, boolean preservesValue
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = SummaryInput::parameter(0) and
(
if range = true
then inputContents = ContentList::element()
else inputContents = ContentList::empty()
then input = SummaryComponentStack::elementOf(SummaryComponentStack::argument(0))
else input = SummaryComponentStack::argument(0)
) and
output = SummaryOutput::thisParameter() and
outputContents = ContentList::element() and
output = SummaryComponentStack::elementOf(SummaryComponentStack::argument(-1)) and
preservesValue = true
}
}
@@ -165,27 +174,27 @@ module EntityFramework {
}
private class RawSqlStringSummarizedCallable extends EFSummarizedCallable {
private SummaryInput input_;
private SummaryOutput output_;
private SummaryComponentStack input_;
private SummaryComponentStack output_;
private boolean preservesValue_;
RawSqlStringSummarizedCallable() {
exists(RawSqlStringStruct s |
this = s.getAConstructor() and
input_ = SummaryInput::parameter(0) and
input_ = SummaryComponentStack::argument(0) and
this.getNumberOfParameters() > 0 and
output_ = SummaryOutput::return() and
output_ = SummaryComponentStack::return() and
preservesValue_ = false
or
this = s.getAConversionTo() and
input_ = SummaryInput::parameter(0) and
output_ = SummaryOutput::return() and
input_ = SummaryComponentStack::argument(0) and
output_ = SummaryComponentStack::return() and
preservesValue_ = false
)
}
override predicate propagatesFlow(
SummaryInput input, SummaryOutput output, boolean preservesValue
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = input_ and
output = output_ and
@@ -295,15 +304,17 @@ module EntityFramework {
* If `t2` is a column type, `c2` will be included in the model (see
* https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations).
*/
private predicate step(Content c1, Type t1, Content c2, Type t2) {
private predicate step(Content c1, Type t1, Content c2, Type t2, int dist) {
exists(Property p1 |
p1 = this.getADbSetProperty(t2) and
c1.(PropertyContent).getProperty() = p1 and
t1 = p1.getType() and
c2 instanceof ElementContent
c2 instanceof ElementContent and
dist = 0
)
or
step(_, _, c1, t1) and
step(_, _, c1, t1, dist - 1) and
dist < DataFlowPrivate::accessPathLimit() - 1 and
not isNotMapped(t2) and
(
// Navigation property (https://docs.microsoft.com/en-us/ef/ef6/fundamentals/relationships)
@@ -350,52 +361,61 @@ module EntityFramework {
* }
* ```
*/
private Property getAColumnProperty() {
Property getAColumnProperty(int dist) {
exists(PropertyContent c, Type t |
this.step(_, _, c, t) and
this.step(_, _, c, t, dist) and
c.getProperty() = result and
isColumnType(t)
)
}
private predicate stepRev(Content c1, Type t1, Content c2, Type t2, int dist) {
step(c1, t1, c2, t2, dist) and
c2.(PropertyContent).getProperty() = getAColumnProperty(dist)
or
stepRev(c2, t2, _, _, dist + 1) and
step(c1, t1, c2, t2, dist)
}
/** Gets a `SaveChanges[Async]` method. */
pragma[nomagic]
SummarizableMethod getASaveChanges() {
Method getASaveChanges() {
this.hasMethod(result) and
result.getName().matches("SaveChanges%")
}
/** Holds if content list `head :: tail` is required. */
predicate requiresContentList(
Content head, Type headType, ContentList tail, Type tailType, Property last
/** Holds if component stack `head :: tail` is required for the input specification. */
predicate requiresComponentStackIn(
Content head, Type headType, SummaryComponentStack tail, int dist
) {
exists(PropertyContent p |
last = this.getAColumnProperty() and
p.getProperty() = last and
tail = ContentList::singleton(p) and
this.step(head, headType, p, tailType)
)
tail = SummaryComponentStack::qualifier() and
this.stepRev(head, headType, _, _, 0) and
dist = -1
or
exists(Content tailHead, ContentList tailTail |
this.requiresContentList(tailHead, tailType, tailTail, _, last) and
tail = ContentList::cons(tailHead, tailTail) and
this.step(head, headType, tailHead, tailType)
exists(Content tailHead, Type tailType, SummaryComponentStack tailTail |
this.requiresComponentStackIn(tailHead, tailType, tailTail, dist - 1) and
tail = SummaryComponentStack::push(SummaryComponent::content(tailHead), tailTail) and
this.stepRev(tailHead, tailType, head, headType, dist)
)
}
/**
* Holds if the access path obtained by concatenating `head` onto `tail`
* is a path from `dbSet` (which is a `DbSet<T>` property belonging to
* this DB context) to `last`, which is a property that is mapped directly
* to a column in the underlying DB.
*/
pragma[noinline]
predicate pathFromDbSetToDbProperty(
Property dbSet, PropertyContent head, ContentList tail, Property last
/** Holds if component stack `head :: tail` is required for the output specification. */
predicate requiresComponentStackOut(
Content head, Type headType, SummaryComponentStack tail, int dist
) {
this.requiresContentList(head, _, tail, _, last) and
head.getProperty() = dbSet and
dbSet = this.getADbSetProperty(_)
exists(Property dbSetProp, PropertyContent c1 |
dbSetProp = this.getADbSetProperty(headType) and
this.stepRev(c1, _, head, headType, 0) and
c1.getProperty() = dbSetProp and
tail = SummaryComponentStack::jump(dbSetProp.getGetter()) and
dist = 0
)
or
exists(Content tailHead, SummaryComponentStack tailTail, Type tailType |
this.requiresComponentStackOut(tailHead, tailType, tailTail, dist - 1) and
tail = SummaryComponentStack::push(SummaryComponent::content(tailHead), tailTail) and
this.stepRev(tailHead, tailType, head, headType, dist)
)
}
}
@@ -404,26 +424,46 @@ module EntityFramework {
DbContextSaveChanges() { this = c.getASaveChanges() }
override predicate requiresContentList(Content head, ContentList tail) {
c.requiresContentList(head, _, tail, _, _)
pragma[noinline]
private predicate input(SummaryComponentStack input, Property mapped) {
exists(PropertyContent head, SummaryComponentStack tail |
c.requiresComponentStackIn(head, _, tail, _) and
head.getProperty() = mapped and
mapped = c.getAColumnProperty(_) and
input = SummaryComponentStack::push(SummaryComponent::content(head), tail)
)
}
pragma[noinline]
private predicate output(SummaryComponentStack output, Property mapped) {
exists(PropertyContent head, SummaryComponentStack tail |
c.requiresComponentStackOut(head, _, tail, _) and
head.getProperty() = mapped and
mapped = c.getAColumnProperty(_) and
output = SummaryComponentStack::push(SummaryComponent::content(head), tail)
)
}
override predicate propagatesFlow(
SummaryInput input, ContentList inputContents, SummaryOutput output,
ContentList outputContents, boolean preservesValue
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
exists(Property mapped |
preservesValue = true and
exists(PropertyContent sourceHead, ContentList sourceTail |
input = SummaryInput::thisParameter() and
c.pathFromDbSetToDbProperty(_, sourceHead, sourceTail, mapped) and
inputContents = ContentList::cons(sourceHead, sourceTail)
) and
exists(Property dbSetProp |
output = SummaryOutput::jump(dbSetProp.getGetter(), SummaryOutput::return()) and
c.pathFromDbSetToDbProperty(dbSetProp, _, outputContents, mapped)
)
input(input, mapped) and
output(output, mapped)
)
}
}
private class DbContextSaveChangesRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
private Content head;
DbContextSaveChangesRequiredSummaryComponentStack() {
any(DbContextClass c).requiresComponentStackIn(head, _, this, _)
or
any(DbContextClass c).requiresComponentStackOut(head, _, this, _)
}
override predicate required(SummaryComponent c) { c = SummaryComponent::content(head) }
}
}