Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cpp/ql/lib/ext/empty.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ extensions:
pack: codeql/cpp-all
extensible: summaryModel
data: []
- addsTo:
pack: codeql/cpp-all
extensible: forwardsModel
data: []
75 changes: 70 additions & 5 deletions cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
* - BarrierGuards:
* `namespace; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance`
* - Forwards:
* `namespace; type; subtypes; name; signature; ext; start; constructor; provenance`
*
* The interpretation of a row is similar to API-graphs with a left-to-right
* reading.
Expand Down Expand Up @@ -160,6 +162,20 @@ predicate summaryModel(
)
}

/**
* Holds if a forward model exists for the given parameters.
*/
predicate forwardsModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string start, string constructor, string provenance, string model
) {
exists(QlBuiltins::ExtensionId madId |
Extensions::forwardsModel(namespace, type, subtypes, name, signature, ext, start, constructor,
provenance, madId) and
model = madId.toString()
)
}

/** Provides a query predicate to check the data for validation errors. */
module ModelValidation {
private string getInvalidModelInput() {
Expand Down Expand Up @@ -259,7 +275,8 @@ private predicate elementSpec(
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
barrierModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
barrierGuardModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
forwardsModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
}

/**
Expand Down Expand Up @@ -596,6 +613,14 @@ private string getAtIndex(string s, int i) {
not (s = "" and i = 0)
}

/** Gets the number of comma-separated arguments in `s`. */
bindingset[s]
private int getNumberOfArguments(string s) {
s = "" and result = 0
or
s != "" and result = count(s.indexOf(",")) + 1
}

/**
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
* number of template arguments in `partiallyNormalizedSignature` with their
Expand All @@ -605,7 +630,7 @@ private string getSignatureWithoutClassTemplateNames(
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
) {
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
remaining = getNumberOfArguments(typeArgs) and
result = partiallyNormalizedSignature
or
exists(string mid |
Expand All @@ -619,7 +644,7 @@ private string getSignatureWithoutClassTemplateNames(
)
or
// Make sure `remaining` is properly bound
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
remaining = [0 .. getNumberOfArguments(typeArgs)] and
not exists(getAtIndex(typeArgs, remaining)) and
result = mid
)
Expand All @@ -636,7 +661,7 @@ pragma[nomagic]
private string getSignatureWithoutFunctionTemplateNames(
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
) {
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
remaining = getNumberOfArguments(nameArgs) and
result =
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
or
Expand All @@ -651,7 +676,7 @@ private string getSignatureWithoutFunctionTemplateNames(
)
or
// Make sure `remaining` is properly bound
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
remaining = [0 .. getNumberOfArguments(nameArgs)] and
not exists(getAtIndex(nameArgs, remaining)) and
result = mid
)
Expand Down Expand Up @@ -1046,6 +1071,46 @@ private module Cached {

import Cached

/** Gets the constructor type selected by `constructorType` in a forwarding model. */
bindingset[forwarder, type, name, constructorType]
private Type getForwardedConstructorType(
Function forwarder, string type, string name, string constructorType
) {
exists(string typeArguments, int index |
parseAngles(type, _, typeArguments, "") and
constructorType = getAtIndex(typeArguments, index) and
result = forwarder.getDeclaringType().getTemplateArgument(index)
)
or
exists(string nameArguments, int index |
parseAngles(name, _, nameArguments, "") and
constructorType = getAtIndex(nameArguments, index) and
result = forwarder.getTemplateArgument(index)
)
}

/** Holds if `forwarder` forwards its arguments starting at `start` to `constructor`. */
predicate forwards(Function forwarder, Constructor constructor, int start) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string startString, string constructorType
|
forwardsModel(namespace, type, subtypes, name, signature, ext, startString, constructorType, _,
_) and
forwarder = interpretElement(namespace, type, subtypes, name, signature, ext) and
start = startString.toInt()
|
// Either the row specifies forwarding to a type given by the type or
// function template, in which case we need to resolve that from the type
// or function name.
constructor.getDeclaringType() =
getForwardedConstructorType(forwarder, type, name, constructorType).getUnspecifiedType()
or
// Or the row specifies forwarding to a specific type.
classHasQualifiedName(constructor.getDeclaringType(), namespace, constructorType)
)
}

/**
* Holds if `node` is specified as a source with the given kind in a MaD flow
* model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ extensible predicate summaryModel(
string input, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
);

/**
* Holds if an external constructor forwarding model exists for the given parameters.
*/
extensible predicate forwardsModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string start, string constructor, string provenance, QlBuiltins::ExtensionId madId
);

/**
* Holds if a neutral model exists for the given parameters.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
)
)
or
argString = "forward" and
result = TForwardPosition()
}

bindingset[token]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ private module Cached {
} or
TSsaSynthNode(SsaImpl::SynthNode n) or
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
TForwarderConstructorArgumentNode(CallInstruction call) {
isForwarderConstructorArgumentNodeImpl(call)
} or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
SsaImpl::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
} or
Expand Down
133 changes: 119 additions & 14 deletions cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,89 @@ private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode
}
}

private Type stripReferences(Type type) {
exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() |
result = unspecifiedType.(Cpp::ReferenceType).getBaseType().getUnspecifiedType()
or
not unspecifiedType instanceof Cpp::ReferenceType and
result = unspecifiedType
)
}

private predicate forwardingCallTargetsConstructor(
CallInstruction call, Cpp::Constructor constructor
) {
exists(int start |
External::forwards(call.getStaticCallTarget(), constructor, start) and
call.getNumberOfPositionalArguments() = start + constructor.getNumberOfParameters() and
forall(int i, Type typeCall, Type typeConstructor |
i = [0 .. constructor.getNumberOfParameters() - 1] and
typeCall = stripReferences(call.getPositionalArgument(start + i).getResultType()) and
typeConstructor = stripReferences(constructor.getParameter(i).getUnspecifiedType())
|
typeCall = typeConstructor
)
)
}

/** Holds if `call` is a call that forwards arguments to a constructor call. */
predicate isForwarderConstructorArgumentNodeImpl(CallInstruction call) {
forwardingCallTargetsConstructor(call, _)
}

/**
* In order to implement a MaD summary for a flow such as:
* ```
* struct Foo {
* int x;
* Foo(int x) { // (2)
* this->x = x;
* }
* }
*
* std::vector<Foo> v;
* int x = source();
* v.emplace_back(x); // (1)
* sink(v.back());
* ```
* we model it as if the code was:
* ```
* v.__emplace_back(x, &Foo)
* ```
* (nevermind that this is not real C++ since you cannot take the address of a
* constructor.)
* where `__emplace_back` invokes `Foo` with the `x` argument and returns the
* result.
*
* This class serves as the argument node for `&Foo`.
*/
private class ForwarderConstructorArgumentNode extends ArgumentNode,
TForwarderConstructorArgumentNode
{
private CallInstruction call;

ForwarderConstructorArgumentNode() { this = TForwarderConstructorArgumentNode(call) }

override predicate sourceArgumentOf(CallInstruction c, ArgumentPosition pos) {
c = call and pos = TForwardPosition()
}

/**
* Gets a constructor which may be targeted by this forwarding call.
*/
Cpp::Constructor getAConstructor() { forwardingCallTargetsConstructor(call, result) }

override DataFlowCallable getEnclosingCallable() {
result.asSourceCallable() = this.getFunction()
}

override Declaration getFunction() { result = call.getEnclosingFunction() }

override Location getLocationImpl() { result = call.getLocation() }

override string toStringImpl() { result = "forwarder for " + call.toString() }
}

/**
* An argument node that is part of a summary. These only occur when the
* summary contains a synthesized call.
Expand Down Expand Up @@ -672,6 +755,12 @@ abstract class Position extends TPosition {
this.getArgumentIndex() = -1 and
result = call.getQualifier()
}

/**
* Holds if this position is the synthetic argument for an address of a
* constructor used for functions which perform "perfect forwarding".
*/
predicate isForward() { none() }
}

class DirectPosition extends Position, TDirectPosition {
Expand Down Expand Up @@ -721,6 +810,16 @@ class FlowSummaryPosition extends Position, TFlowSummaryPosition {
final override int getIndirectionIndex() { result = rk.getIndirectionIndex() }
}

class ForwardPosition extends Position, TForwardPosition {
final override predicate isForward() { any() }

override int getArgumentIndex() { none() }

final override int getIndirectionIndex() { result = 0 }

override string toString() { result = "forward" }
}

newtype TPosition =
TDirectPosition(int argumentIndex) {
exists(any(CallInstruction c).getArgument(argumentIndex))
Expand All @@ -740,6 +839,7 @@ newtype TPosition =
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1]
)
} or
TForwardPosition() or
TFlowSummaryPosition(ReturnKind rk) { FlowSummaryImpl::Private::relevantFlowSummaryPosition(rk) }

private newtype TReturnKind =
Expand Down Expand Up @@ -1258,6 +1358,19 @@ private predicate summarizedCallableIsManual(SummarizedCallable sc) {
sc.asSummarizedCallable().hasManualModel()
}

private DataFlowCallable getTarget(Declaration target) {
// Don't use the source callable if there is a manual model for the target.
not exists(SummarizedCallable sc |
sc.asSummarizedCallable() = target and
summarizedCallableIsManual(sc)
) and
result.asSourceCallable() = target
or
// When there is no function body, or when we have a manual model, dispatch to the summary.
(not target.hasDefinition() or summarizedCallableIsManual(result)) and
result.asSummarizedCallable() = target
}

/**
* A function call relevant for data flow. This includes calls from source
* code and calls inside library callables with a flow summary.
Expand Down Expand Up @@ -1293,20 +1406,7 @@ class DataFlowCall extends TDataFlowCall {
* whether is it manual or generated.
*/
final DataFlowCallable getStaticCallTarget() {
exists(Declaration target | target = this.getStaticCallSourceTarget() |
// Don't use the source callable if there is a manual model for the
// target
not exists(SummarizedCallable sc |
sc.asSummarizedCallable() = target and
summarizedCallableIsManual(sc)
) and
result.asSourceCallable() = target
or
// When there is no function body, or when we have a manual model then
// we dispatch to the summary.
(not target.hasDefinition() or summarizedCallableIsManual(result)) and
result.asSummarizedCallable() = target
)
result = getTarget(this.getStaticCallSourceTarget())
}

/**
Expand Down Expand Up @@ -1493,6 +1593,8 @@ predicate nodeIsHidden(Node n) {
n instanceof SsaSynthNode
or
n.(FlowSummaryNode).getSummaryNode().isHidden()
or
n instanceof ForwarderConstructorArgumentNode
}

predicate neverSkipInPathGraph(Node n) {
Expand Down Expand Up @@ -1574,6 +1676,9 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
kind.isFunctionPointer() and
creation.asInstruction().(FunctionAddressInstruction).getFunctionSymbol() = c.asSourceCallable()
or
kind.isFunctionPointer() and
c = getTarget(creation.(ForwarderConstructorArgumentNode).getAConstructor())
or
kind.isFunctor() and
exists(OperatorCall operator | operator = c.asSourceCallable() |
isFunctorCreationWithoutConstructor(creation, operator)
Expand Down
Loading
Loading