C++: Port implementation to CPP.

This commit is contained in:
Geoffrey White
2024-01-09 11:56:15 +00:00
parent 3aacc5ffde
commit a8863e44db
7 changed files with 119 additions and 133 deletions

View File

@@ -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

View File

@@ -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<DataFlowImplSpecific::SwiftDataFlow> {
module Input implements InputSig<DataFlowImplSpecific::CppDataFlow> {
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,37 +28,40 @@ module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> {
}
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) {
@@ -68,24 +70,12 @@ module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> {
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<DataFlowImplSpecific::SwiftDataFlow> {
// 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<DataFlowImplSpecific::SwiftDataFlow, Input> as Impl
/*private*/ import Make<DataFlowImplSpecific::CppDataFlow, Input> 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<Location>
{
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()
)
}
}

View File

@@ -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" }
}

View File

@@ -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 }

View File

@@ -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.

View File

@@ -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. */

View File

@@ -1588,7 +1588,8 @@ module Make<DF::InputSig DataFlowLang, InputSig<DataFlowLang> 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