mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge pull request #5416 from hvitved/csharp/rework-summaries
C#: Rework flow summary implementation
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a language-independant implementation of static single assignment
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a language-independant implementation of static single assignment
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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())
|
||||
)
|
||||
}
|
||||
@@ -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 + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a language-independant implementation of static single assignment
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a language-independant implementation of static single assignment
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user