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 * The interpretation of a row is similar to API-graphs with a left-to-right
* reading. * reading.
* 1. The `namespace` column selects a package. * 1. The `namespace` column selects a namespace.
* 2. The `type` column selects a type within that package. * 2. The `type` column selects a type within that namespace.
* 3. The `subtypes` is a boolean that indicates whether to jump to an * 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` * arbitrary subtype of that type. Set this to `false` if leaving the `type`
* blank (for example, a free function). * blank (for example, a free function).
@@ -65,16 +65,14 @@
* globally applicable value-preserving step. * globally applicable value-preserving step.
*/ */
import swift import cpp
private import internal.DataFlowDispatch private import new.DataFlow
private import internal.DataFlowPrivate
private import internal.DataFlowPublic
private import internal.FlowSummaryImpl private import internal.FlowSummaryImpl
private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Public
private import internal.FlowSummaryImpl::Private private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImpl::Private::External
private import FlowSummary as FlowSummary
private import codeql.mad.ModelValidation as SharedModelVal private import codeql.mad.ModelValidation as SharedModelVal
private import codeql.util.Unit
/** /**
* A unit class for adding additional source model rows. * A unit class for adding additional source model rows.
@@ -354,13 +352,13 @@ private predicate elementSpec(
private string paramsStringPart(Function c, int i) { private string paramsStringPart(Function c, int i) {
i = -1 and result = "(" and exists(c) i = -1 and result = "(" and exists(c)
or 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 i = 2 * n and result = p
or or
i = 2 * n - 1 and result = "," and n != 0 i = 2 * n - 1 and result = "," and n != 0
) )
or or
i = 2 * c.getNumberOfParams() and result = ")" i = 2 * c.getNumberOfParameters() and result = ")"
} }
/** /**
@@ -401,42 +399,42 @@ private Element interpretElement0(
type = "" and type = "" and
matchesSignature(func, signature) and matchesSignature(func, signature) and
subtypes = false and subtypes = false and
not result instanceof Method and not exists(func.getDeclaringType()) and
result = func result = func
) )
or or
// Member functions // Member functions
exists(NominalTypeDecl namedTypeDecl, Decl declWithMethod, Method method | exists(Class namedClass, Class classWithMethod, Function method |
method.getName() = name and method.getName() = name and
method = declWithMethod.getAMember() and method = classWithMethod.getAMember() and
namedTypeDecl.getFullName() = type and namedClass.getName() = type and
matchesSignature(method, signature) and matchesSignature(method, signature) and
result = method 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 subtypes = true and
declWithMethod.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*() classWithMethod = namedClass.getADerivedClass*()
or or
// member declared directly in the named type (or an extension of it) // member declared directly in the named type
subtypes = false and subtypes = false and
declWithMethod.asNominalTypeDecl() = namedTypeDecl classWithMethod = namedClass
) )
or or
// Fields // Member variables
signature = "" and signature = "" and
exists(NominalTypeDecl namedTypeDecl, Decl declWithField, FieldDecl field | exists(Class namedClass, Class classWithMember, MemberVariable member |
field.getName() = name and member.getName() = name and
field = declWithField.getAMember() and member = classWithMember.getAMember() and
namedTypeDecl.getFullName() = type and namedClass.getName() = type and
result = field result = member
| |
// field declared in the named type or a subtype of it (or an extension of any) // field declared in the named type or a subtype of it (or an extension of any)
subtypes = true and subtypes = true and
declWithField.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*() classWithMember = namedClass.getADerivedClass*()
or or
// field declared directly in the named type (or an extension of it) // field declared directly in the named type (or an extension of it)
subtypes = false and 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 cached
private module Cached { private module Cached {
/** /**
@@ -496,7 +456,7 @@ private module Cached {
* model. * model.
*/ */
cached cached
predicate sourceNode(Node node, string kind) { predicate sourceNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n | exists(SourceSinkInterpretationInput::InterpretNode n |
isSourceNode(n, kind) and n.asNode() = node isSourceNode(n, kind) and n.asNode() = node
) )
@@ -507,7 +467,7 @@ private module Cached {
* model. * model.
*/ */
cached cached
predicate sinkNode(Node node, string kind) { predicate sinkNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n | exists(SourceSinkInterpretationInput::InterpretNode n |
isSinkNode(n, kind) and n.asNode() = node isSinkNode(n, kind) and n.asNode() = node
) )
@@ -567,7 +527,7 @@ private class NeutralCallableAdapter extends NeutralCallable {
string provenance_; string provenance_;
NeutralCallableAdapter() { NeutralCallableAdapter() {
// Neutral models have not been implemented for Swift. // Neutral models have not been implemented for CPP.
none() and none() and
exists(this) and exists(this) and
exists(kind) and exists(kind) and

View File

@@ -2,21 +2,20 @@
* Provides classes and predicates for defining flow summaries. * 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.FlowSummaryImpl
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
private import DataFlowImplSpecific as DataFlowImplSpecific private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import DataFlowImplSpecific::Private private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import DataFlowImplSpecific::Public private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
private import DataFlowImplCommon private import semmle.code.cpp.dataflow.ExternalFlow
private import codeql.swift.dataflow.ExternalFlow
module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> { module Input implements InputSig<DataFlowImplSpecific::CppDataFlow> {
class SummarizedCallableBase = Function; 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() } string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
@@ -29,38 +28,41 @@ module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> {
} }
string encodeContent(ContentSet cs, string arg) { string encodeContent(ContentSet cs, string arg) {
exists(Content::FieldContent c | exists(FieldContent c |
cs.isSingleton(c) and cs.isSingleton(c) and
result = "Field" and result = "Field" and
arg = c.getField().getName() arg = c.getField().getName()
) )
or /*
exists(Content::TupleContent c | * or
cs.isSingleton(c) and * exists(Content::TupleContent c |
result = "TupleElement" and * cs.isSingleton(c) and
arg = c.getIndex().toString() * result = "TupleElement" and
) * arg = c.getIndex().toString()
or * )
exists(Content::EnumContent c, string sig | * or
cs.isSingleton(c) and * exists(Content::EnumContent c, string sig |
sig = c.getSignature() * cs.isSingleton(c) and
| * sig = c.getSignature()
if sig = "some:0" * |
then * if sig = "some:0"
result = "OptionalSome" and * then
arg = "" * result = "OptionalSome" and
else ( * arg = ""
result = "EnumElement" and * else (
arg = sig * result = "EnumElement" and
) * arg = sig
) * )
or * )
exists(Content::CollectionContent c | * or
cs.isSingleton(c) and * exists(Content::CollectionContent c |
result = "CollectionElement" and * cs.isSingleton(c) and
arg = "" * result = "CollectionElement" and
) * arg = ""
} * )
*/
}
string encodeWithoutContent(ContentSet c, string arg) { string encodeWithoutContent(ContentSet c, string arg) {
result = "WithoutContent" + c and arg = "" result = "WithoutContent" + c and arg = ""
@@ -68,24 +70,12 @@ module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> {
string encodeWithContent(ContentSet c, string arg) { result = "WithContent" + c and arg = "" } 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] bindingset[token]
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) { ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
// needed to support `Argument[x..y]` ranges and `Argument[-1]` // needed to support `Argument[x..y]` ranges and `Argument[-1]`
token.getName() = "Argument" and token.getName() = "Argument" and
exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) | exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) |
result.(PositionalParameterPosition).getIndex() = pos result = TDirectPosition(pos)
or
pos = -1 and result instanceof ThisParameterPosition
) )
} }
@@ -94,24 +84,21 @@ module Input implements InputSig<DataFlowImplSpecific::SwiftDataFlow> {
// needed to support `Parameter[x..y]` ranges and `Parameter[-1]` // needed to support `Parameter[x..y]` ranges and `Parameter[-1]`
token.getName() = "Parameter" and token.getName() = "Parameter" and
exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) | exists(int pos | pos = AccessPath::parseInt(token.getAnArgument()) |
result.(PositionalArgumentPosition).getIndex() = pos result = TDirectPosition(pos)
or
pos = -1 and
result instanceof ThisArgumentPosition
) )
} }
} }
private import Make<DataFlowImplSpecific::SwiftDataFlow, Input> as Impl /*private*/ import Make<DataFlowImplSpecific::CppDataFlow, Input> as Impl
private module StepsInput implements Impl::Private::StepsInputSig { 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 module SourceSinkInterpretationInput implements
Impl::Private::External::SourceSinkInterpretationInputSig<Location> Impl::Private::External::SourceSinkInterpretationInputSig<Location>
{ {
class Element = AstNode; class Element = Element;
class SourceOrSinkElement = Element; class SourceOrSinkElement = Element;
@@ -158,10 +145,12 @@ module SourceSinkInterpretationInput implements
DataFlowCall asCall() { this = TDataFlowCall_(result) } DataFlowCall asCall() { this = TDataFlowCall_(result) }
/** Gets the callable that this node corresponds to, if any. */ /** 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. */ /** 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. */ /** Gets a textual representation of this node. */
string toString() { string toString() {
@@ -186,31 +175,31 @@ module SourceSinkInterpretationInput implements
bindingset[c] bindingset[c]
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) { predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
// Allow fields to be picked as output nodes. // Allow fields to be picked as output nodes.
exists(Node n, AstNode ast | exists(Node n, Element ast |
n = node.asNode() and n = node.asNode() and
ast = mid.asElement() ast = mid.asElement()
| |
c = "" and c = "" and
n.asExpr().(MemberRefExpr).getMember() = ast n.asExpr().(VariableAccess).getTarget() = ast
) )
} }
/** Provides additional source specification logic. */ /** Provides additional source specification logic. */
bindingset[c] bindingset[c]
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) { 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 n = node.asNode() and
ast = mid.asElement() and ast = mid.asElement() and
e.getMember() = ast e.getTarget() = ast
| |
// Allow fields to be picked as input nodes. // Allow fields to be picked as input nodes.
c = "" and c = "" and
e.getBase() = n.asExpr() e.getQualifier() = n.asExpr()
or or
// Allow post update nodes to be picked as input nodes when the `input` column // Allow post update nodes to be picked as input nodes when the `input` column
// of the row is `PostUpdate`. // of the row is `PostUpdate`.
c = "PostUpdate" and 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(); abstract string toString();
} }
private class NormalReturnKind extends ReturnKind, TNormalReturnKind { class NormalReturnKind extends ReturnKind, TNormalReturnKind {
int index; int index;
NormalReturnKind() { this = TNormalReturnKind(index) } NormalReturnKind() { this = TNormalReturnKind(index) }
int getIndirectionIndex() { result = index }
override string toString() { result = "indirect return" } override string toString() { result = "indirect return" }
} }

View File

@@ -2330,6 +2330,12 @@ class UnionContent extends Content, TUnionContent {
* stored into (`getAStoreContent`) or read from (`getAReadContent`). * stored into (`getAStoreContent`) or read from (`getAReadContent`).
*/ */
class ContentSet instanceof Content { 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. */ /** Gets a content that may be stored into when storing into this set. */
Content getAStoreContent() { result = this } Content getAStoreContent() { result = this }

View File

@@ -9,6 +9,7 @@
import cpp import cpp
import FunctionInputsAndOutputs import FunctionInputsAndOutputs
import semmle.code.cpp.models.Models 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. * 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. */ /** A data flow source of local user input. */
abstract class LocalFlowSource extends FlowSource { } abstract class LocalFlowSource extends FlowSource { }
/**
* A remote data flow source that is defined through a `RemoteFlowSourceFunction` model.
*/
private class RemoteModelSource extends RemoteFlowSource { private class RemoteModelSource extends RemoteFlowSource {
string sourceType; string sourceType;
@@ -34,6 +37,9 @@ private class RemoteModelSource extends RemoteFlowSource {
override string getSourceType() { result = sourceType } override string getSourceType() { result = sourceType }
} }
/**
* A local data flow source that is defined through a `LocalFlowSourceFunction` model.
*/
private class LocalModelSource extends LocalFlowSource { private class LocalModelSource extends LocalFlowSource {
string sourceType; string sourceType;
@@ -48,6 +54,9 @@ private class LocalModelSource extends LocalFlowSource {
override string getSourceType() { result = sourceType } override string getSourceType() { result = sourceType }
} }
/**
* A local data flow source that the `argv` parameter to `main`.
*/
private class ArgvSource extends LocalFlowSource { private class ArgvSource extends LocalFlowSource {
ArgvSource() { ArgvSource() {
exists(Function main, Parameter argv | exists(Function main, Parameter argv |
@@ -60,6 +69,24 @@ private class ArgvSource extends LocalFlowSource {
override string getSourceType() { result = "a command-line argument" } 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. */ /** A remote data flow sink. */
abstract class RemoteFlowSink extends DataFlow::Node { abstract class RemoteFlowSink extends DataFlow::Node {
/** Gets a string that describes the type of this flow sink. */ /** 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 SourceSinkAccessPath output, int n, InterpretNode ref, InterpretNode node
) { ) {
sourceElementRef(ref, output, _) and 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 = "" if output = ""
then then