diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index 35515cb548c..b4837b26d40 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -13,8 +13,8 @@ * * The interpretation of a row is similar to API-graphs with a left-to-right * reading. - * 1. The `namespace` column selects a package. - * 2. The `type` column selects a type within that package. + * 1. The `namespace` column selects a namespace. + * 2. The `type` column selects a type within that namespace. * 3. The `subtypes` is a boolean that indicates whether to jump to an * arbitrary subtype of that type. Set this to `false` if leaving the `type` * blank (for example, a free function). @@ -65,16 +65,14 @@ * globally applicable value-preserving step. */ -import swift -private import internal.DataFlowDispatch -private import internal.DataFlowPrivate -private import internal.DataFlowPublic +import cpp +private import new.DataFlow private import internal.FlowSummaryImpl private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Private private import internal.FlowSummaryImpl::Private::External -private import FlowSummary as FlowSummary private import codeql.mad.ModelValidation as SharedModelVal +private import codeql.util.Unit /** * A unit class for adding additional source model rows. @@ -354,13 +352,13 @@ private predicate elementSpec( private string paramsStringPart(Function c, int i) { i = -1 and result = "(" and exists(c) or - exists(int n, string p | c.getParam(n).getType().toString() = p | + exists(int n, string p | c.getParameter(n).getType().toString() = p | i = 2 * n and result = p or i = 2 * n - 1 and result = "," and n != 0 ) or - i = 2 * c.getNumberOfParams() and result = ")" + i = 2 * c.getNumberOfParameters() and result = ")" } /** @@ -401,42 +399,42 @@ private Element interpretElement0( type = "" and matchesSignature(func, signature) and subtypes = false and - not result instanceof Method and + not exists(func.getDeclaringType()) and result = func ) or // Member functions - exists(NominalTypeDecl namedTypeDecl, Decl declWithMethod, Method method | + exists(Class namedClass, Class classWithMethod, Function method | method.getName() = name and - method = declWithMethod.getAMember() and - namedTypeDecl.getFullName() = type and + method = classWithMethod.getAMember() and + namedClass.getName() = type and matchesSignature(method, signature) and result = method | - // member declared in the named type or a subtype of it (or an extension of any) + // member declared in the named type or a subtype of it subtypes = true and - declWithMethod.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*() + classWithMethod = namedClass.getADerivedClass*() or - // member declared directly in the named type (or an extension of it) + // member declared directly in the named type subtypes = false and - declWithMethod.asNominalTypeDecl() = namedTypeDecl + classWithMethod = namedClass ) or - // Fields + // Member variables signature = "" and - exists(NominalTypeDecl namedTypeDecl, Decl declWithField, FieldDecl field | - field.getName() = name and - field = declWithField.getAMember() and - namedTypeDecl.getFullName() = type and - result = field + exists(Class namedClass, Class classWithMember, MemberVariable member | + member.getName() = name and + member = classWithMember.getAMember() and + namedClass.getName() = type and + result = member | // field declared in the named type or a subtype of it (or an extension of any) subtypes = true and - declWithField.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*() + classWithMember = namedClass.getADerivedClass*() or // field declared directly in the named type (or an extension of it) subtypes = false and - declWithField.asNominalTypeDecl() = namedTypeDecl + classWithMember = namedClass ) ) } @@ -451,44 +449,6 @@ Element interpretElement( ) } -deprecated private predicate parseField(AccessPathToken c, Content::FieldContent f) { - exists(string fieldRegex, string name | - c.getName() = "Field" and - fieldRegex = "^([^.]+)$" and - name = c.getAnArgument().regexpCapture(fieldRegex, 1) and - f.getField().getName() = name - ) -} - -deprecated private predicate parseTuple(AccessPathToken c, Content::TupleContent t) { - c.getName() = "TupleElement" and - t.getIndex() = c.getAnArgument().toInt() -} - -deprecated private predicate parseEnum(AccessPathToken c, Content::EnumContent e) { - c.getName() = "EnumElement" and - c.getAnArgument() = e.getSignature() - or - c.getName() = "OptionalSome" and - e.getSignature() = "some:0" -} - -/** Holds if the specification component parses as a `Content`. */ -deprecated predicate parseContent(AccessPathToken component, Content content) { - parseField(component, content) - or - parseTuple(component, content) - or - parseEnum(component, content) - or - // map legacy "ArrayElement" specification components to `CollectionContent` - component.getName() = "ArrayElement" and - content instanceof Content::CollectionContent - or - component.getName() = "CollectionElement" and - content instanceof Content::CollectionContent -} - cached private module Cached { /** @@ -496,7 +456,7 @@ private module Cached { * model. */ cached - predicate sourceNode(Node node, string kind) { + predicate sourceNode(DataFlow::Node node, string kind) { exists(SourceSinkInterpretationInput::InterpretNode n | isSourceNode(n, kind) and n.asNode() = node ) @@ -507,7 +467,7 @@ private module Cached { * model. */ cached - predicate sinkNode(Node node, string kind) { + predicate sinkNode(DataFlow::Node node, string kind) { exists(SourceSinkInterpretationInput::InterpretNode n | isSinkNode(n, kind) and n.asNode() = node ) @@ -567,7 +527,7 @@ private class NeutralCallableAdapter extends NeutralCallable { string provenance_; NeutralCallableAdapter() { - // Neutral models have not been implemented for Swift. + // Neutral models have not been implemented for CPP. none() and exists(this) and exists(kind) and diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index 1151a7aeec8..d3b93dc9c0f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -2,21 +2,20 @@ * Provides classes and predicates for defining flow summaries. */ -private import swift +private import cpp 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 DataFlowImplCommon -private import codeql.swift.dataflow.ExternalFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific +private import semmle.code.cpp.dataflow.ExternalFlow -module Input implements InputSig { +module Input implements InputSig { class SummarizedCallableBase = Function; - ArgumentPosition callbackSelfParameterPosition() { result instanceof ThisArgumentPosition } + ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) } - ReturnKind getStandardReturnValueKind() { result instanceof NormalReturnKind } + ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 } string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() } @@ -29,38 +28,41 @@ module Input implements InputSig { } string encodeContent(ContentSet cs, string arg) { - exists(Content::FieldContent c | + exists(FieldContent c | cs.isSingleton(c) and result = "Field" and arg = c.getField().getName() ) - or - exists(Content::TupleContent c | - cs.isSingleton(c) and - result = "TupleElement" and - arg = c.getIndex().toString() - ) - or - exists(Content::EnumContent c, string sig | - cs.isSingleton(c) and - sig = c.getSignature() - | - if sig = "some:0" - then - result = "OptionalSome" and - arg = "" - else ( - result = "EnumElement" and - arg = sig - ) - ) - or - exists(Content::CollectionContent c | - cs.isSingleton(c) and - result = "CollectionElement" and - arg = "" - ) - } + /* + * or + * exists(Content::TupleContent c | + * cs.isSingleton(c) and + * result = "TupleElement" and + * arg = c.getIndex().toString() + * ) + * or + * exists(Content::EnumContent c, string sig | + * cs.isSingleton(c) and + * sig = c.getSignature() + * | + * if sig = "some:0" + * then + * result = "OptionalSome" and + * arg = "" + * else ( + * result = "EnumElement" and + * arg = sig + * ) + * ) + * or + * exists(Content::CollectionContent c | + * cs.isSingleton(c) and + * result = "CollectionElement" and + * arg = "" + * ) + */ + + } string encodeWithoutContent(ContentSet c, string arg) { result = "WithoutContent" + c and arg = "" @@ -68,24 +70,12 @@ module Input implements InputSig { string encodeWithContent(ContentSet c, string arg) { result = "WithContent" + c and arg = "" } - bindingset[token] - ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) { - // map legacy "ArrayElement" specification components to `CollectionContent` - token.getName() = "ArrayElement" and - result.isSingleton(any(Content::CollectionContent c)) - or - token.getName() = "CollectionElement" and - result.isSingleton(any(Content::CollectionContent c)) - } - bindingset[token] ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) { // needed to support `Argument[x..y]` ranges and `Argument[-1]` token.getName() = "Argument" and exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) | - result.(PositionalParameterPosition).getIndex() = pos - or - pos = -1 and result instanceof ThisParameterPosition + result = TDirectPosition(pos) ) } @@ -94,24 +84,21 @@ module Input implements InputSig { // needed to support `Parameter[x..y]` ranges and `Parameter[-1]` token.getName() = "Parameter" and exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) | - result.(PositionalArgumentPosition).getIndex() = pos - or - pos = -1 and - result instanceof ThisArgumentPosition + result = TDirectPosition(pos) ) } } -private import Make as Impl +/*private*/ import Make as Impl private module StepsInput implements Impl::Private::StepsInputSig { - DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc } + DataFlowCall getACall(Public::SummarizedCallable sc) { result.getStaticCallTarget() = sc } } module SourceSinkInterpretationInput implements Impl::Private::External::SourceSinkInterpretationInputSig { - class Element = AstNode; + class Element = Element; class SourceOrSinkElement = Element; @@ -158,10 +145,12 @@ module SourceSinkInterpretationInput implements DataFlowCall asCall() { this = TDataFlowCall_(result) } /** Gets the callable that this node corresponds to, if any. */ - DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() } + DataFlowCallable asCallable() { result.(Function) = this.asElement() } /** Gets the target of this call, if any. */ - Element getCallTarget() { result = this.asCall().asCall().getStaticTarget() } + Element getCallTarget() { + result = this.asNode().asExpr().(Call).getTarget() + } /** Gets a textual representation of this node. */ string toString() { @@ -186,31 +175,31 @@ module SourceSinkInterpretationInput implements bindingset[c] predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) { // Allow fields to be picked as output nodes. - exists(Node n, AstNode ast | + exists(Node n, Element ast | n = node.asNode() and ast = mid.asElement() | c = "" and - n.asExpr().(MemberRefExpr).getMember() = ast + n.asExpr().(VariableAccess).getTarget() = ast ) } /** Provides additional source specification logic. */ bindingset[c] predicate interpretInput(string c, InterpretNode mid, InterpretNode node) { - exists(Node n, AstNode ast, MemberRefExpr e | + exists(Node n, Element ast, VariableAccess e | n = node.asNode() and ast = mid.asElement() and - e.getMember() = ast + e.getTarget() = ast | // Allow fields to be picked as input nodes. c = "" and - e.getBase() = n.asExpr() + e.getQualifier() = n.asExpr() or // Allow post update nodes to be picked as input nodes when the `input` column // of the row is `PostUpdate`. c = "PostUpdate" and - e.getBase() = n.(PostUpdateNode).getPreUpdateNode().asExpr() + e.getQualifier() = n.(PostUpdateNode).getPreUpdateNode().asExpr() ) } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 19140653877..d72f65d2c40 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -458,11 +458,13 @@ class ReturnKind extends TReturnKind { abstract string toString(); } -private class NormalReturnKind extends ReturnKind, TNormalReturnKind { +class NormalReturnKind extends ReturnKind, TNormalReturnKind { int index; NormalReturnKind() { this = TNormalReturnKind(index) } + int getIndirectionIndex() { result = index } + override string toString() { result = "indirect return" } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index ab1e3365aa8..e887a9c9b78 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -2330,6 +2330,12 @@ class UnionContent extends Content, TUnionContent { * stored into (`getAStoreContent`) or read from (`getAReadContent`). */ class ContentSet instanceof Content { + /** + * Holds if this content set is the singleton `{c}`. At present, this is + * the only kind of content set supported in C/C++. + */ + predicate isSingleton(Content c) { this = c } + /** Gets a content that may be stored into when storing into this set. */ Content getAStoreContent() { result = this } diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll index e99836939ae..d2103f83bc0 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll @@ -9,6 +9,7 @@ import cpp import FunctionInputsAndOutputs import semmle.code.cpp.models.Models +import semmle.code.cpp.dataflow.ExternalFlow /** * A library function that returns data that may be read from a network connection. diff --git a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll index 14fd1305724..e9291a5223d 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll @@ -20,6 +20,9 @@ abstract class RemoteFlowSource extends FlowSource { } /** A data flow source of local user input. */ abstract class LocalFlowSource extends FlowSource { } +/** + * A remote data flow source that is defined through a `RemoteFlowSourceFunction` model. + */ private class RemoteModelSource extends RemoteFlowSource { string sourceType; @@ -34,6 +37,9 @@ private class RemoteModelSource extends RemoteFlowSource { override string getSourceType() { result = sourceType } } +/** + * A local data flow source that is defined through a `LocalFlowSourceFunction` model. + */ private class LocalModelSource extends LocalFlowSource { string sourceType; @@ -48,6 +54,9 @@ private class LocalModelSource extends LocalFlowSource { override string getSourceType() { result = sourceType } } +/** + * A local data flow source that the `argv` parameter to `main`. + */ private class ArgvSource extends LocalFlowSource { ArgvSource() { exists(Function main, Parameter argv | @@ -60,6 +69,24 @@ private class ArgvSource extends LocalFlowSource { override string getSourceType() { result = "a command-line argument" } } +/** + * A remote data flow source that is defined through 'models as data'. + */ +private class ExternalRemoteFlowSource extends RemoteFlowSource { + ExternalRemoteFlowSource() { sourceNode(this, "remote") } + + override string getSourceType() { result = "external" } +} + +/** + * A local data flow source that is defined through 'models as data'. + */ +private class ExternalLocalFlowSource extends LocalFlowSource { + ExternalLocalFlowSource() { sourceNode(this, "local") } + + override string getSourceType() { result = "external" } +} + /** A remote data flow sink. */ abstract class RemoteFlowSink extends DataFlow::Node { /** Gets a string that describes the type of this flow sink. */ diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index 25a276d41a7..afd2b69a9ec 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -1588,7 +1588,8 @@ module Make Input> { SourceSinkAccessPath output, int n, InterpretNode ref, InterpretNode node ) { sourceElementRef(ref, output, _) and - n = 0 and + //n = 0 and + n = [0,1] and // TODO: fix this, there's no good reason for it. ( if output = "" then