C#: Use FlowSummaryImpl from dataflow pack

This commit is contained in:
Tom Hvitved
2023-11-24 10:28:33 +01:00
parent 28373e0fdf
commit a2093c9aa2
14 changed files with 516 additions and 2186 deletions

View File

@@ -55,7 +55,6 @@
], ],
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [ "DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll", "go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll" "swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
@@ -466,7 +465,6 @@
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll" "ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
], ],
"AccessPathSyntax": [ "AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll", "go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll", "java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll", "python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",

View File

@@ -1,199 +1,23 @@
/** Provides classes and predicates for defining flow summaries. */ /** Provides classes and predicates for defining flow summaries. */
import csharp import csharp
private import dotnet
private import internal.FlowSummaryImpl as Impl private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowDispatch as DataFlowDispatch private import internal.DataFlowDispatch as DataFlowDispatch
private import Impl::Public::SummaryComponent as SummaryComponentInternal
class ParameterPosition = DataFlowDispatch::ParameterPosition; deprecated class ParameterPosition = DataFlowDispatch::ParameterPosition;
class ArgumentPosition = DataFlowDispatch::ArgumentPosition; deprecated class ArgumentPosition = DataFlowDispatch::ArgumentPosition;
// import all instances below deprecated class SummaryComponent = Impl::Private::SummaryComponent;
private module Summaries {
private import semmle.code.csharp.frameworks.EntityFramework
}
class SummaryComponent = Impl::Public::SummaryComponent; deprecated module SummaryComponent = Impl::Private::SummaryComponent;
/** Provides predicates for constructing summary components. */ deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
module SummaryComponent {
predicate content = SummaryComponentInternal::content/1;
/** Gets a summary component for parameter `i`. */ deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
SummaryComponent parameter(int i) {
exists(ArgumentPosition pos |
result = SummaryComponentInternal::parameter(pos) and
i = pos.getPosition()
)
}
/** Gets a summary component for argument `i`. */ deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
SummaryComponent argument(int i) {
exists(ParameterPosition pos |
result = SummaryComponentInternal::argument(pos) and
i = pos.getPosition()
)
}
predicate return = SummaryComponentInternal::return/1;
/** Gets a summary component that represents a qualifier. */
SummaryComponent qualifier() {
exists(ParameterPosition pos |
result = SummaryComponentInternal::argument(pos) and
pos.isThisParameter()
)
}
/** Gets a summary component that represents an element in a collection. */
SummaryComponent element() { result = content(any(DataFlow::ElementContent c)) }
/** Gets a summary component for property `p`. */
SummaryComponent property(Property p) {
result = content(any(DataFlow::PropertyContent c | c.getProperty() = p.getUnboundDeclaration()))
}
/** 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(DataFlowDispatch::NormalReturnKind rk)) }
predicate syntheticGlobal = SummaryComponentInternal::syntheticGlobal/1;
class SyntheticGlobal = SummaryComponentInternal::SyntheticGlobal;
}
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
/** Provides predicates for constructing stacks of summary components. */
module SummaryComponentStack {
private import Impl::Public::SummaryComponentStack as SummaryComponentStackInternal
predicate singleton = SummaryComponentStackInternal::singleton/1;
predicate push = SummaryComponentStackInternal::push/2;
/** Gets a singleton stack for argument `i`. */
SummaryComponentStack argument(int i) { result = singleton(SummaryComponent::argument(i)) }
predicate return = SummaryComponentStackInternal::return/1;
/** Gets a singleton stack representing a qualifier. */
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
/** Gets a stack representing an element of `container`. */
SummaryComponentStack elementOf(SummaryComponentStack container) {
result = push(SummaryComponent::element(), container)
}
/** Gets a stack representing a property `p` of `object`. */
SummaryComponentStack propertyOf(Property p, SummaryComponentStack object) {
result = push(SummaryComponent::property(p), object)
}
/** 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 a singleton stack representing a synthetic global with name `name`. */
SummaryComponentStack syntheticGlobal(string synthetic) {
result = singleton(SummaryComponent::syntheticGlobal(synthetic))
}
/**
* DEPRECATED: Use the member predicate `getMadRepresentation` instead.
*
* Gets a textual representation of this stack used for flow summaries.
*/
deprecated string getComponentStack(SummaryComponentStack s) { result = s.getMadRepresentation() }
}
class SummarizedCallable = Impl::Public::SummarizedCallable; class SummarizedCallable = Impl::Public::SummarizedCallable;
private predicate recordConstructorFlow(Constructor c, int i, Property p) {
c = any(RecordType r).getAMember() and
exists(string name |
c.getParameter(i).getName() = name and
c.getDeclaringType().getAMember(name) = p
)
}
private class RecordConstructorFlow extends SummarizedCallable {
RecordConstructorFlow() { recordConstructorFlow(this, _, _) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
exists(int i, Property p |
recordConstructorFlow(this, i, p) and
input = SummaryComponentStack::argument(i) and
output = SummaryComponentStack::propertyOf(p, SummaryComponentStack::return()) and
preservesValue = true
)
}
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
private class RecordConstructorFlowRequiredSummaryComponentStack extends RequiredSummaryComponentStack
{
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
exists(Property p |
recordConstructorFlow(_, _, p) and
head = SummaryComponent::property(p) and
tail = SummaryComponentStack::return()
)
}
}
class Provenance = Impl::Public::Provenance; class Provenance = Impl::Public::Provenance;
private import semmle.code.csharp.frameworks.system.linq.Expressions
private SummaryComponent delegateSelf() {
exists(ArgumentPosition pos |
result = SummaryComponentInternal::parameter(pos) and
pos.isDelegateSelf()
)
}
private predicate mayInvokeCallback(Callable c, int n) {
c.getParameter(n).getType() instanceof SystemLinqExpressions::DelegateExtType and
not c.hasBody() and
(if c instanceof Accessor then not c.fromSource() else any())
}
private class SummarizedCallableWithCallback extends SummarizedCallable {
private int pos;
SummarizedCallableWithCallback() { mayInvokeCallback(this, pos) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = SummaryComponentStack::argument(pos) and
output = SummaryComponentStack::push(delegateSelf(), input) and
preservesValue = true
}
override predicate hasProvenance(Provenance provenance) { provenance = "hq-generated" }
}
private class RequiredComponentStackForCallback extends RequiredSummaryComponentStack {
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
exists(int pos |
mayInvokeCallback(_, pos) and
head = delegateSelf() and
tail = SummaryComponentStack::argument(pos)
)
}
}

View File

@@ -1,182 +0,0 @@
/**
* Module for parsing access paths from MaD models, both the identifying access path used
* by dynamic languages, and the input/output specifications for summary steps.
*
* This file is used by the shared data flow library and by the JavaScript libraries
* (which does not use the shared data flow libraries).
*/
/**
* Convenience-predicate for extracting two capture groups at once.
*/
bindingset[input, regexp]
private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) {
capture1 = input.regexpCapture(regexp, 1) and
capture2 = input.regexpCapture(regexp, 2)
}
/** Companion module to the `AccessPath` class. */
module AccessPath {
/** A string that should be parsed as an access path. */
abstract class Range extends string {
bindingset[this]
Range() { any() }
}
/**
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
* of the constant or any value contained in the interval.
*/
bindingset[arg]
int parseInt(string arg) {
result = arg.toInt()
or
// Match "n1..n2"
exists(string lo, string hi |
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
result = [lo.toInt() .. hi.toInt()]
)
}
/**
* Parses a lower-bounded interval `n..` and gets the lower bound.
*/
bindingset[arg]
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
/**
* Parses an integer constant or interval (bounded or unbounded) that explicitly
* references the arity, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
private int parseIntWithExplicitArity(string arg, int arity) {
result >= 0 and // do not allow N-1 to resolve to a negative index
exists(string lo |
// N-x
lo = arg.regexpCapture("N-(\\d+)", 1) and
result = arity - lo.toInt()
or
// N-x..
lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and
result = [arity - lo.toInt(), arity - 1]
)
or
exists(string lo, string hi |
// x..N-y
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [lo.toInt() .. arity - hi.toInt()]
or
// N-x..N-y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. arity - hi.toInt()] and
result >= 0
or
// N-x..y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. hi.toInt()] and
result >= 0
)
}
/**
* Parses an integer constant or interval (bounded or unbounded) and gets any
* of the integers contained within (of which there may be infinitely many).
*
* Has no result for arguments involving an explicit arity, such as `N-1`.
*/
bindingset[arg, result]
int parseIntUnbounded(string arg) {
result = parseInt(arg)
or
result >= parseLowerBound(arg)
}
/**
* Parses an integer constant or interval (bounded or unbounded) that
* may reference the arity of a call, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
int parseIntWithArity(string arg, int arity) {
result = parseInt(arg)
or
result in [parseLowerBound(arg) .. arity - 1]
or
result = parseIntWithExplicitArity(arg, arity)
}
}
/** Gets the `n`th token on the access path as a string. */
private string getRawToken(AccessPath path, int n) {
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
// Instead use regexpFind to match valid tokens, and supplement with a final length
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
}
/**
* A string that occurs as an access path (either identifying or input/output spec)
* which might be relevant for this database.
*/
class AccessPath extends string instanceof AccessPath::Range {
/** Holds if this string is not a syntactically valid access path. */
predicate hasSyntaxError() {
// If the lengths match, all characters must haven been included in a token
// or seen by the `.` lookahead pattern.
this != "" and
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
}
/** Gets the `n`th token on the access path (if there are no syntax errors). */
AccessPathToken getToken(int n) {
result = getRawToken(this, n) and
not this.hasSyntaxError()
}
/** Gets the number of tokens on the path (if there are no syntax errors). */
int getNumToken() {
result = count(int n | exists(getRawToken(this, n))) and
not this.hasSyntaxError()
}
}
/**
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
*/
class AccessPathToken extends string {
AccessPathToken() { this = getRawToken(_, _) }
private string getPart(int part) {
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
}
/** Gets the name of the token, such as `Member` from `Member[x]` */
string getName() { result = this.getPart(1) }
/**
* Gets the argument list, such as `1,2` from `Member[1,2]`,
* or has no result if there are no arguments.
*/
string getArgumentList() { result = this.getPart(2) }
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
pragma[nomagic]
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument() { result = this.getArgument(_) }
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument(string name) { result = this.getArgument(name, _) }
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
}

View File

@@ -116,9 +116,7 @@ private module Cached {
// No need to include calls that are compiled from source // No need to include calls that are compiled from source
not call.getImplementation().getMethod().compiledFromSource() not call.getImplementation().getMethod().compiledFromSource()
} or } or
TSummaryCall( TSummaryCall(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) {
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
} }
@@ -446,7 +444,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
* the method `Select`. * the method `Select`.
*/ */
class SummaryCall extends DelegateDataFlowCall, TSummaryCall { class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable c; private FlowSummary::SummarizedCallable c;
private FlowSummaryImpl::Private::SummaryNode receiver; private FlowSummaryImpl::Private::SummaryNode receiver;
SummaryCall() { this = TSummaryCall(c, receiver) } SummaryCall() { this = TSummaryCall(c, receiver) }

View File

@@ -676,11 +676,11 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
f instanceof InstanceFieldOrProperty f instanceof InstanceFieldOrProperty
or or
exists( exists(
FlowSummaryImpl::Public::SummarizedCallable sc, FlowSummaryImpl::Private::SummarizedCallableImpl sc,
FlowSummaryImpl::Public::SummaryComponentStack input FlowSummaryImpl::Private::SummaryComponentStack input
| |
sc.propagatesFlow(input, _, _) and sc.propagatesFlow(input, _, _) and
input.contains(FlowSummary::SummaryComponent::content(f.getContent())) input.contains(FlowSummaryImpl::Private::SummaryComponent::content(f.getContent()))
) )
) )
| |
@@ -1393,11 +1393,11 @@ private module ArgumentNodes {
} }
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNodeImpl { private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNodeImpl {
private DataFlowCall call_; private SummaryCall call_;
private ArgumentPosition pos_; private ArgumentPosition pos_;
SummaryArgumentNode() { SummaryArgumentNode() {
FlowSummaryImpl::Private::summaryArgumentNode(call_, this.getSummaryNode(), pos_) FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
} }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
@@ -1683,11 +1683,11 @@ private module OutNodes {
} }
private class SummaryOutNode extends FlowSummaryNode, OutNode { private class SummaryOutNode extends FlowSummaryNode, OutNode {
private DataFlowCall call; private SummaryCall call;
private ReturnKind kind_; private ReturnKind kind_;
SummaryOutNode() { SummaryOutNode() {
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), kind_) FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
} }
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ } override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
@@ -1700,7 +1700,7 @@ import OutNodes
class FlowSummaryNode extends NodeImpl, TFlowSummaryNode { class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { FlowSummary::SummarizedCallable getSummarizedCallable() {
result = this.getSummaryNode().getSummarizedCallable() result = this.getSummaryNode().getSummarizedCallable()
} }
@@ -2424,7 +2424,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* by default as a heuristic. * by default as a heuristic.
*/ */
predicate allowParameterReturnInSelf(ParameterNode p) { predicate allowParameterReturnInSelf(ParameterNode p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p) exists(DataFlowCallable c, ParameterPosition pos |
parameterNode(p, c, pos) and
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
)
} }
/** An approximated `Content`. */ /** An approximated `Content`. */

View File

@@ -88,13 +88,13 @@
import csharp import csharp
import ExternalFlowExtensions import ExternalFlowExtensions
private import AccessPathSyntax
private import DataFlowDispatch private import DataFlowDispatch
private import DataFlowPrivate private import DataFlowPrivate
private import DataFlowPublic private import DataFlowPublic
private import FlowSummaryImpl
private import FlowSummaryImpl::Public private import FlowSummaryImpl::Public
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Private::External private import FlowSummaryImpl::Private::External
private import FlowSummaryImplSpecific
private import semmle.code.csharp.commons.QualifiedName private import semmle.code.csharp.commons.QualifiedName
private import codeql.mad.ModelValidation as SharedModelVal private import codeql.mad.ModelValidation as SharedModelVal
@@ -154,6 +154,21 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
/** Provides a query predicate to check the MaD models for validation errors. */ /** Provides a query predicate to check the MaD models for validation errors. */
module ModelValidation { module ModelValidation {
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
private predicate getRelevantAccessPath(string path) {
summaryModel(_, _, _, _, _, _, path, _, _, _) or
summaryModel(_, _, _, _, _, _, _, path, _, _) or
sinkModel(_, _, _, _, _, _, path, _, _) or
sourceModel(_, _, _, _, _, _, path, _, _)
}
private module MkAccessPath = AccessPathSyntax::AccessPath<getRelevantAccessPath/1>;
class AccessPath = MkAccessPath::AccessPath;
class AccessPathToken = MkAccessPath::AccessPathToken;
private string getInvalidModelInput() { private string getInvalidModelInput() {
exists(string pred, AccessPath input, AccessPathToken part | exists(string pred, AccessPath input, AccessPathToken part |
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink" sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
@@ -380,14 +395,14 @@ Declaration interpretElement(
* A callable where there exists a MaD sink model that applies to it. * A callable where there exists a MaD sink model that applies to it.
*/ */
class SinkCallable extends Callable { class SinkCallable extends Callable {
SinkCallable() { sinkElement(this, _, _, _) } SinkCallable() { SourceSinkInterpretationInput::sinkElement(this, _, _) }
} }
/** /**
* A callable where there exists a MaD source model that applies to it. * A callable where there exists a MaD source model that applies to it.
*/ */
class SourceCallable extends Callable { class SourceCallable extends Callable {
SourceCallable() { sourceElement(this, _, _, _) } SourceCallable() { SourceSinkInterpretationInput::sourceElement(this, _, _) }
} }
cached cached
@@ -398,7 +413,9 @@ private module Cached {
*/ */
cached cached
predicate sourceNode(Node node, string kind) { predicate sourceNode(Node node, string kind) {
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node) exists(SourceSinkInterpretationInput::InterpretNode n |
isSourceNode(n, kind) and n.asNode() = node
)
} }
/** /**
@@ -407,7 +424,9 @@ private module Cached {
*/ */
cached cached
predicate sinkNode(Node node, string kind) { predicate sinkNode(Node node, string kind) {
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node) exists(SourceSinkInterpretationInput::InterpretNode n |
isSinkNode(n, kind) and n.asNode() = node
)
} }
} }
@@ -485,3 +504,63 @@ string asPartialNeutralModel(UnboundCallable c) {
+ parameters + ";" // + parameters + ";" //
) )
} }
private predicate interpretSummary(
UnboundCallable c, string input, string output, string kind, string provenance
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
c = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
private class SummarizedCallableAdapter extends SummarizedCallable {
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
private predicate relevantSummaryElementManual(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isManual()
)
}
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isGenerated()
)
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(string kind |
this.relevantSummaryElementManual(input, output, kind)
or
not this.relevantSummaryElementManual(_, _, _) and
this.relevantSummaryElementGenerated(input, output, kind)
|
if kind = "value" then preservesValue = true else preservesValue = false
)
}
override predicate hasProvenance(Provenance provenance) {
interpretSummary(this, _, _, _, provenance)
}
}
private class NeutralCallableAdapter extends NeutralCallable {
string kind;
string provenance_;
NeutralCallableAdapter() {
exists(string namespace, string type, string name, string signature |
neutralModel(namespace, type, name, signature, kind, provenance_) and
this = interpretElement(namespace, type, false, name, signature, "")
)
}
override string getKind() { result = kind }
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
}

View File

@@ -1,340 +0,0 @@
/**
* Provides C# specific classes and predicates for defining flow summaries.
*/
private import csharp
private import dotnet
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import DataFlowDispatch
private import DataFlowPrivate
private import DataFlowPublic
private import DataFlowImplCommon
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import semmle.code.csharp.Unification
private import ExternalFlow
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
/**
* A class of callables that are candidates for flow summary modeling.
*/
class SummarizedCallableBase = UnboundCallable;
/**
* A class of callables that are candidates for neutral modeling.
*/
class NeutralCallableBase = UnboundCallable;
/**
* A module for importing frameworks that define synthetic globals.
*/
private module SyntheticGlobals {
private import semmle.code.csharp.frameworks.EntityFramework
}
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
/** Gets the parameter position of the instance parameter. */
ArgumentPosition callbackSelfParameterPosition() { result.isDelegateSelf() }
/** Gets the synthesized data-flow call for `receiver`. */
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
/** Gets the type of content `c`. */
DataFlowType getContentType(Content c) {
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
t = c.(FieldContent).getField().getType()
or
t = c.(PropertyContent).getProperty().getType()
or
t = c.(SyntheticFieldContent).getField().getType()
or
c instanceof ElementContent and
t instanceof ObjectType // we don't know what the actual element type is
)
}
/** Gets the type of the parameter at the given position. */
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) {
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
exists(int i |
pos.getPosition() = i and
t = c.getParameter(i).getType()
)
or
pos.isThisParameter() and
t = c.getDeclaringType()
)
}
/** Gets the return type of kind `rk` for callable `c`. */
DataFlowType getReturnType(DotNet::Callable c, ReturnKind rk) {
exists(Type t | result.asGvnType() = 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 type of the parameter matching arguments at position `pos` in a
* synthesized call that targets a callback of type `t`.
*/
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) {
exists(SystemLinqExpressions::DelegateExtType dt |
t.asGvnType() = Gvn::getGlobalValueNumber(dt) and
result.asGvnType() =
Gvn::getGlobalValueNumber(dt.getDelegateType().getParameter(pos.getPosition()).getType())
)
or
pos.isDelegateSelf() and
result = t
}
/**
* 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.asGvnType() = Gvn::getGlobalValueNumber(dt) and
result.asGvnType() = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
)
}
/** Gets the type of synthetic global `sg`. */
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) {
exists(sg) and
result.asGvnType() = Gvn::getGlobalValueNumber(any(ObjectType t))
}
/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
*/
predicate summaryElement(Callable c, string input, string output, string kind, string provenance) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
c = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
/**
* Holds if a neutral model exists for `c` of kind `kind`
* and with provenance `provenance`.
*/
predicate neutralElement(Callable c, string kind, string provenance) {
exists(string namespace, string type, string name, string signature |
neutralModel(namespace, type, name, signature, kind, provenance) and
c = interpretElement(namespace, type, false, name, signature, "")
)
}
/**
* Holds if an external source specification exists for `e` with output specification
* `output`, kind `kind`, and provenance `provenance`.
*/
predicate sourceElement(Element e, string output, string kind, string provenance) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
/**
* Holds if an external sink specification exists for `e` with input specification
* `input`, kind `kind` and provenance `provenance`.
*/
predicate sinkElement(Element e, string input, string kind, string provenance) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
/** Gets the summary component for specification component `c`, if any. */
bindingset[c]
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
c = "Element" and result = SummaryComponent::content(any(ElementContent ec))
or
c = "WithoutElement" and result = SummaryComponent::withoutContent(any(ElementContent ec))
or
c = "WithElement" and result = SummaryComponent::withContent(any(ElementContent ec))
or
// Qualified names may contain commas,such as in `Tuple<,>`, so get the entire argument list
// rather than an individual argument.
exists(Field f |
c.getName() = "Field" and
c.getArgumentList() = f.getFullyQualifiedName() and
result = SummaryComponent::content(any(FieldContent fc | fc.getField() = f))
)
or
exists(Property p |
c.getName() = "Property" and
c.getArgumentList() = p.getFullyQualifiedName() and
result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p))
)
or
exists(SyntheticField f |
c.getAnArgument("SyntheticField") = f and
result = SummaryComponent::content(any(SyntheticFieldContent sfc | sfc.getField() = f))
)
}
/** Gets the textual representation of the content in the format used for MaD models. */
private string getContentSpecific(Content c) {
c = TElementContent() and result = "Element"
or
exists(Field f | c = TFieldContent(f) and result = "Field[" + f.getFullyQualifiedName() + "]")
or
exists(Property p |
c = TPropertyContent(p) and result = "Property[" + p.getFullyQualifiedName() + "]"
)
or
exists(SyntheticField f | c = TSyntheticFieldContent(f) and result = "SyntheticField[" + f + "]")
}
/** Gets the textual representation of a summary component in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
or
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"
or
sc = TWithContentSummaryComponent(_) and result = "WithElement"
or
exists(OutRefReturnKind rk |
sc = TReturnSummaryComponent(rk) and
result = "Argument[" + rk.getPosition() + "]"
)
}
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
string getParameterPosition(ParameterPosition pos) {
result = pos.getPosition().toString()
or
pos.isThisParameter() and
result = "this"
or
pos.isDelegateSelf() and
result = "delegate-self"
}
/** Gets the textual representation of an argument position in the format used for flow summaries. */
string getArgumentPosition(ArgumentPosition pos) {
result = pos.getPosition().toString()
or
pos.isQualifier() and
result = "this"
or
pos.isDelegateSelf() and
result = "delegate-self"
}
/** Holds if input specification component `c` needs a reference. */
predicate inputNeedsReferenceSpecific(string c) { none() }
/** Holds if output specification component `c` needs a reference. */
predicate outputNeedsReferenceSpecific(string c) { none() }
class SourceOrSinkElement = Element;
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
NormalReturnKind getReturnValueKind() { any() }
private newtype TInterpretNode =
TElement_(Element n) or
TNode_(Node n) or
TDataFlowCall_(DataFlowCall c)
/** An entity used to interpret a source/sink specification. */
class InterpretNode extends TInterpretNode {
/** Gets the element that this node corresponds to, if any. */
SourceOrSinkElement asElement() { this = TElement_(result) }
/** Gets the data-flow node that this node corresponds to, if any. */
Node asNode() { this = TNode_(result) }
/** Gets the call that this node corresponds to, if any. */
DataFlowCall asCall() { this = TDataFlowCall_(result) }
/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
/** Gets the target of this call, if any. */
Callable getCallTarget() { result = this.asCall().(NonDelegateDataFlowCall).getATarget(_) }
/** Gets a textual representation of this node. */
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toString()
or
result = this.asCall().toString()
}
/** Gets the location of this node. */
Location getLocation() {
result = this.asElement().getLocation()
or
result = this.asNode().getLocation()
or
result = this.asCall().getLocation()
}
}
/** Provides additional sink specification logic required for attributes. */
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
exists(Node n | n = node.asNode() |
(c = "Parameter" or c = "") and
n.asParameter() = mid.asElement()
or
c = "" and
n.asExpr().(AssignableRead).getTarget().getUnboundDeclaration() = mid.asElement()
)
}
/** Provides additional sink specification logic required for attributes. */
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
c = "" and
exists(Assignable a |
n.asNode().asExpr() = a.getAnAssignedValue() and
a.getUnboundDeclaration() = mid.asElement()
)
}
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
bindingset[s]
ArgumentPosition parseParamBody(string s) {
result.getPosition() = AccessPath::parseInt(s)
or
s = "this" and
result.isQualifier()
or
s = "delegate-self" and
result.isDelegateSelf()
}
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
bindingset[s]
ParameterPosition parseArgBody(string s) {
result.getPosition() = AccessPath::parseInt(s)
or
s = "this" and
result.isThisParameter()
or
s = "delegate-self" and
result.isDelegateSelf()
}

View File

@@ -9,7 +9,8 @@ private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.system.data.Entity private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.system.collections.Generic private import semmle.code.csharp.frameworks.system.collections.Generic
private import semmle.code.csharp.frameworks.Sql private import semmle.code.csharp.frameworks.Sql
private import semmle.code.csharp.dataflow.FlowSummary private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Public
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
/** /**
@@ -85,9 +86,23 @@ module EntityFramework {
} }
/** A flow summary for EntityFramework. */ /** A flow summary for EntityFramework. */
abstract class EFSummarizedCallable extends SummarizedCallable { abstract class EFSummarizedCallable extends SummarizedCallableImpl {
bindingset[this] bindingset[this]
EFSummarizedCallable() { any() } EFSummarizedCallable() { any() }
override predicate hasProvenance(Provenance provenance) { provenance = "manual" }
}
// see `SummarizedCallableImpl` qldoc
private class EFSummarizedCallableAdapter extends SummarizedCallable instanceof EFSummarizedCallable
{
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
none()
}
override predicate hasProvenance(Provenance provenance) {
EFSummarizedCallable.super.hasProvenance(provenance)
}
} }
/** The class ``Microsoft.EntityFrameworkCore.DbQuery`1`` or ``System.Data.Entity.DbQuery`1``. */ /** The class ``Microsoft.EntityFrameworkCore.DbQuery`1`` or ``System.Data.Entity.DbQuery`1``. */

View File

@@ -1,15 +1,2 @@
import csharp import csharp
import semmle.code.csharp.dataflow.internal.ExternalFlow import semmle.code.csharp.dataflow.internal.ExternalFlow::ModelValidation
import semmle.code.csharp.dataflow.internal.AccessPathSyntax
import ModelValidation
private predicate getRelevantAccessPath(string path) {
summaryModel(_, _, _, _, _, _, path, _, _, _) or
summaryModel(_, _, _, _, _, _, _, path, _, _) or
sinkModel(_, _, _, _, _, _, path, _, _) or
sourceModel(_, _, _, _, _, _, path, _, _)
}
private class AccessPathsExternal extends AccessPath::Range {
AccessPathsExternal() { getRelevantAccessPath(this) }
}

View File

@@ -1,13 +1,11 @@
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.internal.ExternalFlow
import shared.FlowSummaries import shared.FlowSummaries
import semmle.code.csharp.dataflow.internal.ExternalFlow
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable { private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
IncludeAllSummarizedCallable() { exists(this) } IncludeAllSummarizedCallable() { exists(this) }
} }
private class IncludeNeutralSummarizedCallable extends RelevantNeutralCallable instanceof FlowSummaryImpl::Public::NeutralSummaryCallable private class IncludeNeutralSummarizedCallable extends RelevantNeutralCallable {
{
/** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */ /** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */
final override string getCallableCsv() { result = asPartialNeutralModel(this) } final override string getCallableCsv() { result = asPartialNeutralModel(this) }
} }

View File

@@ -1,8 +1,9 @@
import shared.FlowSummaries import shared.FlowSummaries
private import semmle.code.csharp.dataflow.internal.ExternalFlow private import semmle.code.csharp.dataflow.internal.ExternalFlow
class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable instanceof SummarizedCallable class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable {
{ IncludeFilteredSummarizedCallable() { exists(this) }
/** /**
* Holds if flow is propagated between `input` and `output` and * Holds if flow is propagated between `input` and `output` and
* if there is no summary for a callable in a `base` class or interface * if there is no summary for a callable in a `base` class or interface
@@ -14,7 +15,7 @@ class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable instan
super.propagatesFlow(input, output, preservesValue) and super.propagatesFlow(input, output, preservesValue) and
not exists(IncludeSummarizedCallable rsc | not exists(IncludeSummarizedCallable rsc |
isBaseCallableOrPrototype(rsc) and isBaseCallableOrPrototype(rsc) and
rsc.(SummarizedCallable).propagatesFlow(input, output, preservesValue) and rsc.propagatesFlow(input, output, preservesValue) and
this.(UnboundCallable).overridesOrImplementsUnbound(rsc) this.(UnboundCallable).overridesOrImplementsUnbound(rsc)
) )
} }

View File

@@ -1,5 +1,6 @@
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework import csharp
import shared.FlowSummaries import shared.FlowSummaries
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework
import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow
private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable instanceof EFSummarizedCallable private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable instanceof EFSummarizedCallable

View File

@@ -1,4 +1,5 @@
import semmle.code.csharp.dataflow.FlowSummary import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Public
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
private import semmle.code.csharp.dataflow.internal.ExternalFlow private import semmle.code.csharp.dataflow.internal.ExternalFlow