Skip to content

Commit 79ac009

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

3 files changed

Lines changed: 188 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: 149 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,120 @@ 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+
bindingset[source, sc]
174+
SourceSinkReportingElement getASourceReportingElement(
175+
Input::SourceBase source, Impl::Private::SummaryComponent sc
176+
) {
177+
exists(Call call | call.getTarget() = source |
178+
sc = Impl::Private::SummaryComponent::return(_) and
179+
result = call
180+
or
181+
exists(ArgumentPosition pos |
182+
sc = Impl::Private::SummaryComponent::argument(pos) and
183+
result = pos.getArgument(call)
184+
)
185+
)
186+
or
187+
exists(ParameterPosition pos |
188+
sc = Impl::Private::SummaryComponent::parameter(pos) and
189+
result = pos.getParameter(source)
190+
)
191+
}
192+
193+
pragma[nomagic]
194+
private IndirectReturnOutNode getIndirectReturn(CallInstruction call, NormalReturnKind rk) {
195+
result.getCallInstruction() = call and
196+
pragma[only_bind_out](result.getIndirectionIndex()) =
197+
pragma[only_bind_out](rk.getIndirectionIndex())
198+
}
199+
200+
bindingset[e, sc]
201+
Node getSourceDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
202+
exists(DataFlowCall call |
203+
exists(ArgumentPosition pos |
204+
sc = Impl::Private::SummaryComponent::argument(pos) and
205+
pos.getArgument(call.asCallInstruction().getUnconvertedResultExpression()) = e
206+
|
207+
pos.getIndirectionIndex() = 0 and
208+
result.(PostUpdateNode).getPreUpdateNode().asExpr() = e
209+
or
210+
result.(PostUpdateNode).getPreUpdateNode().asIndirectExpr(pos.getIndirectionIndex()) = e
211+
)
212+
or
213+
exists(ReturnKind rk |
214+
sc = Impl::Private::SummaryComponent::return(rk) and
215+
e = call.asCallInstruction().getUnconvertedResultExpression()
216+
|
217+
rk.getIndirectionIndex() = 0 and
218+
simpleOutNode(result, call.asCallInstruction())
219+
or
220+
result = getIndirectReturn(call.asCallInstruction(), rk)
221+
)
222+
)
223+
or
224+
exists(ParameterPosition pos, ParameterNode p |
225+
sc = Impl::Private::SummaryComponent::parameter(pos) and
226+
p.isParameterOf(e.getEnclosingCallable(), pos) and
227+
result = p
228+
)
229+
}
142230

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

145-
SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) { none() }
242+
bindingset[e, sc]
243+
Node getSinkDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
244+
exists(ArgumentPosition pos, CallInstruction call |
245+
sc = Impl::Private::SummaryComponent::argument(pos) and
246+
pos.getArgument(call.getUnconvertedResultExpression()) = e and
247+
result.(ArgumentNode).sourceArgumentOf(call, pos)
248+
)
146249
}
147250
}
148251

@@ -319,3 +422,41 @@ module Private {
319422
}
320423

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

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)