Skip to content

Commit 70be52a

Browse files
committed
C++: Add a synthetic parameter for the lambda for forward functions and implement forwarding.
1 parent 5251c65 commit 70be52a

2 files changed

Lines changed: 105 additions & 14 deletions

File tree

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ private module Cached {
191191
} or
192192
TSsaSynthNode(SsaImpl::SynthNode n) or
193193
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
194+
TForwarderConstructorArgumentNode(CallInstruction call) {
195+
isForwarderConstructorArgumentNodeImpl(call)
196+
} or
194197
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
195198
SsaImpl::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
196199
} or

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,89 @@ private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode
593593
}
594594
}
595595

596+
private Type stripReferences(Type type) {
597+
exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() |
598+
result = unspecifiedType.(Cpp::ReferenceType).getBaseType().getUnspecifiedType()
599+
or
600+
not unspecifiedType instanceof Cpp::ReferenceType and
601+
result = unspecifiedType
602+
)
603+
}
604+
605+
private predicate forwardingCallTargetsConstructor(
606+
CallInstruction call, Cpp::Constructor constructor
607+
) {
608+
exists(int start |
609+
External::forwards(call.getStaticCallTarget(), constructor, start) and
610+
call.getNumberOfPositionalArguments() = start + constructor.getNumberOfParameters() and
611+
forall(int i, Type typeCall, Type typeConstructor |
612+
i = [0 .. constructor.getNumberOfParameters() - 1] and
613+
typeCall = stripReferences(call.getPositionalArgument(start + i).getResultType()) and
614+
typeConstructor = stripReferences(constructor.getParameter(i).getUnspecifiedType())
615+
|
616+
typeCall = typeConstructor
617+
)
618+
)
619+
}
620+
621+
/** Holds if `call` is a call that forwards arguments to a constructor call. */
622+
predicate isForwarderConstructorArgumentNodeImpl(CallInstruction call) {
623+
forwardingCallTargetsConstructor(call, _)
624+
}
625+
626+
/**
627+
* In order to implement a MaD summary for a flow such as:
628+
* ```
629+
* struct Foo {
630+
* int x;
631+
* Foo(int x) { // (2)
632+
* this->x = x;
633+
* }
634+
* }
635+
*
636+
* std::vector<Foo> v;
637+
* int x = source();
638+
* v.emplace_back(x); // (1)
639+
* sink(v.back());
640+
* ```
641+
* we model it as if the code was:
642+
* ```
643+
* v.__emplace_back(x, &Foo)
644+
* ```
645+
* (nevermind that this is not real C++ since you cannot take the address of a
646+
* constructor.)
647+
* where `__emplace_back` invokes `Foo` with the `x` argument and returns the
648+
* result.
649+
*
650+
* This class serves as the argument node for `&Foo`.
651+
*/
652+
private class ForwarderConstructorArgumentNode extends ArgumentNode,
653+
TForwarderConstructorArgumentNode
654+
{
655+
private CallInstruction call;
656+
657+
ForwarderConstructorArgumentNode() { this = TForwarderConstructorArgumentNode(call) }
658+
659+
override predicate sourceArgumentOf(CallInstruction c, ArgumentPosition pos) {
660+
c = call and pos = TForwardPosition()
661+
}
662+
663+
/**
664+
* Gets a constructor which may be targeted by this forwarding call.
665+
*/
666+
Cpp::Constructor getAConstructor() { forwardingCallTargetsConstructor(call, result) }
667+
668+
override DataFlowCallable getEnclosingCallable() {
669+
result.asSourceCallable() = this.getFunction()
670+
}
671+
672+
override Declaration getFunction() { result = call.getEnclosingFunction() }
673+
674+
override Location getLocationImpl() { result = call.getLocation() }
675+
676+
override string toStringImpl() { result = "forwarder for " + call.toString() }
677+
}
678+
596679
/**
597680
* An argument node that is part of a summary. These only occur when the
598681
* summary contains a synthesized call.
@@ -1275,6 +1358,19 @@ private predicate summarizedCallableIsManual(SummarizedCallable sc) {
12751358
sc.asSummarizedCallable().hasManualModel()
12761359
}
12771360

1361+
private DataFlowCallable getTarget(Declaration target) {
1362+
// Don't use the source callable if there is a manual model for the target.
1363+
not exists(SummarizedCallable sc |
1364+
sc.asSummarizedCallable() = target and
1365+
summarizedCallableIsManual(sc)
1366+
) and
1367+
result.asSourceCallable() = target
1368+
or
1369+
// When there is no function body, or when we have a manual model, dispatch to the summary.
1370+
(not target.hasDefinition() or summarizedCallableIsManual(result)) and
1371+
result.asSummarizedCallable() = target
1372+
}
1373+
12781374
/**
12791375
* A function call relevant for data flow. This includes calls from source
12801376
* code and calls inside library callables with a flow summary.
@@ -1310,20 +1406,7 @@ class DataFlowCall extends TDataFlowCall {
13101406
* whether is it manual or generated.
13111407
*/
13121408
final DataFlowCallable getStaticCallTarget() {
1313-
exists(Declaration target | target = this.getStaticCallSourceTarget() |
1314-
// Don't use the source callable if there is a manual model for the
1315-
// target
1316-
not exists(SummarizedCallable sc |
1317-
sc.asSummarizedCallable() = target and
1318-
summarizedCallableIsManual(sc)
1319-
) and
1320-
result.asSourceCallable() = target
1321-
or
1322-
// When there is no function body, or when we have a manual model then
1323-
// we dispatch to the summary.
1324-
(not target.hasDefinition() or summarizedCallableIsManual(result)) and
1325-
result.asSummarizedCallable() = target
1326-
)
1409+
result = getTarget(this.getStaticCallSourceTarget())
13271410
}
13281411

13291412
/**
@@ -1510,6 +1593,8 @@ predicate nodeIsHidden(Node n) {
15101593
n instanceof SsaSynthNode
15111594
or
15121595
n.(FlowSummaryNode).getSummaryNode().isHidden()
1596+
or
1597+
n instanceof ForwarderConstructorArgumentNode
15131598
}
15141599

15151600
predicate neverSkipInPathGraph(Node n) {
@@ -1591,6 +1676,9 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
15911676
kind.isFunctionPointer() and
15921677
creation.asInstruction().(FunctionAddressInstruction).getFunctionSymbol() = c.asSourceCallable()
15931678
or
1679+
kind.isFunctionPointer() and
1680+
c = getTarget(creation.(ForwarderConstructorArgumentNode).getAConstructor())
1681+
or
15941682
kind.isFunctor() and
15951683
exists(OperatorCall operator | operator = c.asSourceCallable() |
15961684
isFunctorCreationWithoutConstructor(creation, operator)

0 commit comments

Comments
 (0)