mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
382 lines
13 KiB
Plaintext
382 lines
13 KiB
Plaintext
/**
|
|
* Provides classes and predicates for defining flow summaries.
|
|
*/
|
|
|
|
private import csharp
|
|
private import semmle.code.csharp.frameworks.system.linq.Expressions
|
|
private import codeql.dataflow.internal.FlowSummaryImpl
|
|
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
|
|
private import DataFlowImplSpecific as DataFlowImplSpecific
|
|
private import DataFlowImplSpecific::Private
|
|
private import DataFlowImplSpecific::Public
|
|
private import semmle.code.csharp.Unification
|
|
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
|
|
|
module Input implements InputSig<Location, DataFlowImplSpecific::CsharpDataFlow> {
|
|
class SummarizedCallableBase = UnboundCallable;
|
|
|
|
ArgumentPosition callbackSelfParameterPosition() { result.isDelegateSelf() }
|
|
|
|
ReturnKind getStandardReturnValueKind() { result instanceof NormalReturnKind }
|
|
|
|
string encodeParameterPosition(ParameterPosition pos) {
|
|
result = pos.getPosition().toString()
|
|
or
|
|
pos.isThisParameter() and
|
|
result = "this"
|
|
or
|
|
pos.isDelegateSelf() and
|
|
result = "delegate-self"
|
|
}
|
|
|
|
string encodeArgumentPosition(ArgumentPosition pos) {
|
|
result = pos.getPosition().toString()
|
|
or
|
|
pos.isQualifier() and
|
|
result = "this"
|
|
or
|
|
pos.isDelegateSelf() and
|
|
result = "delegate-self"
|
|
}
|
|
|
|
string encodeContent(ContentSet c, string arg) {
|
|
c = TElementContent() and result = "Element" and arg = ""
|
|
or
|
|
exists(Field f | c = TFieldContent(f) and result = "Field" and arg = f.getFullyQualifiedName())
|
|
or
|
|
exists(Property p |
|
|
c = TPropertyContent(p) and result = "Property" and arg = p.getFullyQualifiedName()
|
|
)
|
|
or
|
|
exists(SyntheticField f |
|
|
c = TSyntheticFieldContent(f) and result = "SyntheticField" and arg = f
|
|
)
|
|
}
|
|
|
|
string encodeWithoutContent(ContentSet c, string arg) {
|
|
result = "WithoutElement" and
|
|
c = TElementContent() and
|
|
arg = ""
|
|
}
|
|
|
|
string encodeWithContent(ContentSet c, string arg) {
|
|
result = "WithElement" and
|
|
c = TElementContent() and
|
|
arg = ""
|
|
}
|
|
|
|
bindingset[token]
|
|
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
|
// needed to support `Argument[x..y]` ranges
|
|
token.getName() = "Argument" and
|
|
result.getPosition() = AccessPath::parseInt(token.getAnArgument())
|
|
}
|
|
|
|
bindingset[token]
|
|
ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) {
|
|
// needed to support `Parameter[x..y]` ranges
|
|
token.getName() = "Parameter" and
|
|
result.getPosition() = AccessPath::parseInt(token.getAnArgument())
|
|
}
|
|
}
|
|
|
|
private import Make<Location, DataFlowImplSpecific::CsharpDataFlow, Input> as Impl
|
|
|
|
private module TypesInput implements Impl::Private::TypesInputSig {
|
|
DataFlowType getSyntheticGlobalType(Impl::Private::SyntheticGlobal sg) {
|
|
exists(sg) and
|
|
result.asGvnType() = Gvn::getGlobalValueNumber(any(ObjectType t))
|
|
}
|
|
|
|
DataFlowType getContentType(ContentSet 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
|
|
)
|
|
}
|
|
|
|
DataFlowType getParameterType(Impl::Public::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()
|
|
)
|
|
}
|
|
|
|
DataFlowType getReturnType(Impl::Public::SummarizedCallable 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()
|
|
)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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())
|
|
)
|
|
}
|
|
}
|
|
|
|
private module StepsInput implements Impl::Private::StepsInputSig {
|
|
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
|
sc = viableCallable(result).asSummarizedCallable()
|
|
}
|
|
}
|
|
|
|
module SourceSinkInterpretationInput implements
|
|
Impl::Private::External::SourceSinkInterpretationInputSig
|
|
{
|
|
private import csharp as Cs
|
|
|
|
class Element = Cs::Element;
|
|
|
|
predicate sourceElement(Element e, string output, string kind) {
|
|
exists(
|
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
|
|
|
|
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, _) and
|
|
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
|
)
|
|
}
|
|
|
|
predicate sinkElement(Element e, string input, string kind) {
|
|
exists(
|
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
|
|
|
|
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, _) and
|
|
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
|
)
|
|
}
|
|
|
|
class SourceOrSinkElement = Element;
|
|
|
|
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. */
|
|
Element 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. */
|
|
bindingset[c]
|
|
predicate interpretOutput(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 source specification logic. */
|
|
bindingset[c]
|
|
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) {
|
|
c = "" and
|
|
exists(Assignable a |
|
|
node.asNode().asExpr() = a.getAnAssignedValue() and
|
|
a.getUnboundDeclaration() = mid.asElement()
|
|
)
|
|
}
|
|
}
|
|
|
|
module Private {
|
|
import Impl::Private
|
|
import Impl::Private::Types<TypesInput>
|
|
|
|
module Steps = Impl::Private::Steps<StepsInput>;
|
|
|
|
module External {
|
|
import Impl::Private::External
|
|
import Impl::Private::External::SourceSinkInterpretation<SourceSinkInterpretationInput>
|
|
}
|
|
|
|
private module SummaryComponentInternal = Impl::Private::SummaryComponent;
|
|
|
|
/** Provides predicates for constructing summary components. */
|
|
module SummaryComponent {
|
|
predicate content = SummaryComponentInternal::content/1;
|
|
|
|
/** Gets a summary component for parameter `i`. */
|
|
SummaryComponent parameter(int i) {
|
|
exists(ArgumentPosition pos |
|
|
result = SummaryComponentInternal::parameter(pos) and
|
|
i = pos.getPosition()
|
|
)
|
|
}
|
|
|
|
/** Gets a summary component for argument `i`. */
|
|
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(NormalReturnKind rk)) }
|
|
|
|
predicate syntheticGlobal = SummaryComponentInternal::syntheticGlobal/1;
|
|
|
|
class SyntheticGlobal = Impl::Private::SyntheticGlobal;
|
|
}
|
|
|
|
private module SummaryComponentStackInternal = Impl::Private::SummaryComponentStack;
|
|
|
|
/** Provides predicates for constructing stacks of summary components. */
|
|
module SummaryComponentStack {
|
|
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))
|
|
}
|
|
}
|
|
}
|
|
|
|
module Public = Impl::Public;
|
|
|
|
// import all instances below
|
|
private module BidirectionalImports {
|
|
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
|
private import semmle.code.csharp.frameworks.EntityFramework
|
|
}
|
|
|
|
private import semmle.code.csharp.frameworks.system.linq.Expressions
|
|
|
|
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 Public::SummarizedCallable {
|
|
private int pos;
|
|
|
|
SummarizedCallableWithCallback() { mayInvokeCallback(this, pos) }
|
|
|
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
|
input = "Argument[" + pos + "]" and
|
|
output = "Argument[" + pos + "].Parameter[delegate-self]" and
|
|
preservesValue = true
|
|
}
|
|
|
|
override predicate hasProvenance(Public::Provenance provenance) { provenance = "hq-generated" }
|
|
}
|