Skip to content

Commit 05a9f0c

Browse files
committed
C++: Support for access path at sources and sinks.
1 parent 2ea969d commit 05a9f0c

3 files changed

Lines changed: 185 additions & 14 deletions

File tree

cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
import cpp
114114
private import new.DataFlow
115115
private import semmle.code.cpp.controlflow.IRGuards
116+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes as Nodes
116117
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
117118
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
118119
private import internal.FlowSummaryImpl
@@ -952,9 +953,7 @@ private module Cached {
952953
*/
953954
cached
954955
predicate sourceNode(DataFlow::Node node, string kind, string model) {
955-
exists(SourceSinkInterpretationInput::InterpretNode n |
956-
isSourceNode(n, kind, model) and n.asNode() = node
957-
)
956+
node.(Nodes::FlowSummaryNode).isSource(kind, model)
958957
}
959958

960959
/**
@@ -963,9 +962,7 @@ private module Cached {
963962
*/
964963
cached
965964
predicate sinkNode(DataFlow::Node node, string kind, string model) {
966-
exists(SourceSinkInterpretationInput::InterpretNode n |
967-
isSinkNode(n, kind, model) and n.asNode() = node
968-
)
965+
node.(Nodes::FlowSummaryNode).isSink(kind, model)
969966
}
970967

971968
private newtype TKindModelPair =

cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 146 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
1717

1818
class SummarizedCallableBase = Function;
1919

20-
class SourceBase extends Void {
21-
Location getLocation() { none() }
22-
}
20+
class SourceBase = Function;
2321

24-
class SinkBase = SourceBase;
22+
class SinkBase = Function;
2523

2624
class FlowSummaryCallBase = CallInstruction;
2725

@@ -134,15 +132,117 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
134132

135133
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
136134

135+
private class ConversionCall extends Call {
136+
ConversionCall() { this.getTarget() instanceof ConversionOperator }
137+
}
138+
137139
private module Input2 implements Impl::Private::InputSig2 {
138140
private import codeql.util.Void
139141

140-
class SourceSinkReportingElement extends Void {
141-
Location getLocation() { none() }
142+
class SourceSinkReportingElement extends Element {
143+
SourceSinkReportingElement() { this instanceof Expr or this instanceof Parameter }
144+
145+
DataFlowCallable getEnclosingCallable() {
146+
result.asSourceCallable() =
147+
[this.(Expr).getEnclosingFunction(), this.(Parameter).getFunction()]
148+
}
149+
150+
/**
151+
* Gets the member function corresponding to an overloaded `operator()` when this element is
152+
* invoked.
153+
*/
154+
private MemberFunction getOperatorCallFunction() {
155+
// An `operator()` on a struct
156+
result.getClassAndName("operator()").getADerivedClass*() = this.(Expr).getUnspecifiedType()
157+
or
158+
// A lambda that has undergone "lambda to function-pointer conversion"
159+
result = this.(ConversionCall).getQualifier().(LambdaExpression).getLambdaFunction()
160+
}
161+
162+
SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) {
163+
exists(ParameterPosition pos | sc = Impl::Private::SummaryComponent::parameter(pos) |
164+
// Taking the address of a function
165+
result = pos.getParameter(this.(FunctionAccess).getTarget())
166+
or
167+
// Passing an object with an overloaded `operator()`
168+
result = pos.getParameter(this.getOperatorCallFunction())
169+
)
170+
}
171+
}
172+
173+
SourceSinkReportingElement getASourceReportingElement(
174+
Input::SourceBase source, Impl::Private::SummaryComponent sc
175+
) {
176+
exists(Call call | call.getTarget() = source |
177+
sc = Impl::Private::SummaryComponent::return(_) and
178+
result = call
179+
or
180+
exists(ArgumentPosition pos |
181+
sc = Impl::Private::SummaryComponent::argument(pos) and
182+
result = pos.getArgument(call)
183+
)
184+
)
185+
or
186+
exists(ParameterPosition pos |
187+
sc = Impl::Private::SummaryComponent::parameter(pos) and
188+
result = pos.getParameter(source)
189+
)
190+
}
191+
192+
pragma[nomagic]
193+
private IndirectReturnOutNode getIndirectReturn(CallInstruction call, NormalReturnKind rk) {
194+
result.getCallInstruction() = call and
195+
pragma[only_bind_out](result.getIndirectionIndex()) =
196+
pragma[only_bind_out](rk.getIndirectionIndex())
197+
}
198+
199+
bindingset[e, sc]
200+
Node getSourceDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
201+
exists(DataFlowCall call |
202+
exists(ArgumentPosition pos |
203+
sc = Impl::Private::SummaryComponent::argument(pos) and
204+
pos.getArgument(call.asCallInstruction().getUnconvertedResultExpression()) = e
205+
|
206+
pos.getIndirectionIndex() = 0 and
207+
result.(PostUpdateNode).getPreUpdateNode().asExpr() = e
208+
or
209+
result.(PostUpdateNode).getPreUpdateNode().asIndirectExpr(pos.getIndirectionIndex()) = e
210+
)
211+
or
212+
exists(ReturnKind rk |
213+
sc = Impl::Private::SummaryComponent::return(rk) and
214+
e = call.asCallInstruction().getUnconvertedResultExpression()
215+
|
216+
rk.getIndirectionIndex() = 0 and
217+
simpleOutNode(result, call.asCallInstruction())
218+
or
219+
result = getIndirectReturn(call.asCallInstruction(), rk)
220+
)
221+
)
222+
or
223+
exists(ParameterPosition pos, ParameterNode p |
224+
sc = Impl::Private::SummaryComponent::parameter(pos) and
225+
p.isParameterOf(e.getEnclosingCallable(), pos) and
226+
result = p
227+
)
228+
}
142229

143-
DataFlowCallable getEnclosingCallable() { none() }
230+
SourceSinkReportingElement getASinkReportingElement(
231+
Input::SinkBase sink, Impl::Private::SummaryComponent sc
232+
) {
233+
exists(Call call, ArgumentPosition pos |
234+
call.getTarget() = sink and
235+
sc = Impl::Private::SummaryComponent::argument(pos) and
236+
result = pos.getArgument(call)
237+
)
238+
}
144239

145-
SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) { none() }
240+
Node getSinkDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
241+
exists(ArgumentPosition pos, CallInstruction call |
242+
sc = Impl::Private::SummaryComponent::argument(pos) and
243+
pos.getArgument(call.getUnconvertedResultExpression()) = e and
244+
result.(ArgumentNode).sourceArgumentOf(call, pos)
245+
)
146246
}
147247
}
148248

@@ -319,3 +419,41 @@ module Private {
319419
}
320420

321421
module Public = Impl::Public;
422+
423+
private class SourceModelFunction extends Public::SourceElement instanceof Function {
424+
private string namespace;
425+
private string type;
426+
private boolean subtypes;
427+
private string name;
428+
private string signature;
429+
private string ext;
430+
431+
SourceModelFunction() {
432+
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
433+
this = interpretElement(namespace, type, subtypes, name, signature, ext)
434+
}
435+
436+
override predicate isSource(
437+
string output, string kind, Public::Provenance provenance, string model
438+
) {
439+
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model)
440+
}
441+
}
442+
443+
private class SinkModelFunction extends Public::SinkElement instanceof Function {
444+
private string namespace;
445+
private string type;
446+
private boolean subtypes;
447+
private string name;
448+
private string signature;
449+
private string ext;
450+
451+
SinkModelFunction() {
452+
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
453+
this = interpretElement(namespace, type, subtypes, name, signature, ext)
454+
}
455+
456+
override predicate isSink(string input, string kind, Public::Provenance provenance, string model) {
457+
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, model)
458+
}
459+
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,42 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
15411541
override Location getLocationImpl() { result = this.getSummaryNode().getLocation() }
15421542

15431543
override string toStringImpl() { result = this.getSummaryNode().toString() }
1544+
1545+
/** Gets the source element that this node belongs to, if any. */
1546+
FlowSummaryImpl::Public::SourceElement getSourceElement() {
1547+
result = this.getSummaryNode().getSourceElement()
1548+
}
1549+
1550+
/** Gets the sink element that this node belongs to, if any. */
1551+
FlowSummaryImpl::Public::SinkElement getSinkElement() {
1552+
result = this.getSummaryNode().getSinkElement()
1553+
}
1554+
1555+
/** Holds if this node is a source node of kind `kind`. */
1556+
predicate isSource(string kind, string model) {
1557+
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind, model)
1558+
}
1559+
1560+
/** Holds if this node is a sink node of kind `kind`. */
1561+
predicate isSink(string kind, string model) {
1562+
this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind, model)
1563+
}
1564+
}
1565+
1566+
private class SourceOutputNode extends FlowSummaryImpl::Private::SourceOutputNode {
1567+
final override string toString() {
1568+
exists(Call call |
1569+
this.isOutArgument(call) and
1570+
result = call.getTarget() + " output argument"
1571+
)
1572+
or
1573+
not this.isOutArgument(_) and
1574+
result = super.toString()
1575+
}
1576+
1577+
private predicate isOutArgument(Call call) {
1578+
[call.getAnArgument(), call.getQualifier()] = this.getSourceSinkReportingElement()
1579+
}
15441580
}
15451581

15461582
/**

0 commit comments

Comments
 (0)