Skip to content

Commit 782f1fc

Browse files
authored
Merge pull request #22374 from MathiasVP/cpp-access-paths-for-sources-and-sinks-3
C++: Support access paths for sources and sinks
2 parents f2ce282 + 86f7ccf commit 782f1fc

15 files changed

Lines changed: 1002 additions & 329 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Sources and sinks defined using models-as-data now support access paths with fields. For example, the path `ReturnValue.Field[S::f]` makes the field `S::f` a flow source when it is returned by a call.

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: 221 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,192 @@ 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+
pragma[nomagic]
143+
private predicate hasFunctionAndIndirectionIndex(
144+
Function f, int indirectionIndex, Ssa::ExplicitDefinition def
145+
) {
146+
def.getFunction() = f and
147+
def.getSourceVariable().getIRVariable() instanceof IRReturnVariable and
148+
def.getIndirectionIndex() = indirectionIndex
149+
}
150+
151+
/** Holds if `def` defines `e` as a returned value with return kind `rk`. */
152+
bindingset[rk, e]
153+
private predicate isReturnExpr(Function f, ReturnKind rk, Expr e) {
154+
exists(Ssa::ExplicitDefinition def |
155+
hasFunctionAndIndirectionIndex(f, rk.getIndirectionIndex(), def) and
156+
e =
157+
def.getAssignedInstruction()
158+
.(StoreInstruction)
159+
.getSourceValue()
160+
.getUnconvertedResultExpression()
161+
)
162+
}
163+
164+
private MemberFunction getFunctionFromType(Expr e) {
165+
result.getClassAndName("operator()").getADerivedClass*() = e.getUnspecifiedType()
166+
}
167+
168+
private Function getFunctionFromExpr(Expr e) {
169+
result = e.(FunctionAccess).getTarget()
170+
or
171+
result = e.(ConversionCall).getQualifier().(LambdaExpression).getLambdaFunction()
172+
}
173+
174+
class SourceSinkReportingElement extends Element {
175+
SourceSinkReportingElement() { this instanceof Expr or this instanceof Parameter }
176+
177+
DataFlowCallable getEnclosingCallable() {
178+
result.asSourceCallable() =
179+
[this.(Expr).getEnclosingFunction(), this.(Parameter).getFunction()]
180+
}
181+
182+
/** Gets the function invoked when this element is used as a callback. */
183+
private Function getCallable() {
184+
// The expression is a struct which implements `operator()`.
185+
result = getFunctionFromType(this)
186+
or
187+
// The expression is a function pointer
188+
result = getFunctionFromExpr(this)
189+
or
190+
// The expression is an SSA read of an assignment of a callable
191+
exists(Ssa::Definition def |
192+
def.getAUse().getDef().getUnconvertedResultExpression() = this and
193+
result =
194+
getFunctionFromExpr(def.getAnUltimateDefinition()
195+
.(Ssa::DirectExplicitDefinition)
196+
.getAssignedInstruction()
197+
.(StoreInstruction)
198+
.getSourceValue()
199+
.getUnconvertedResultExpression())
200+
)
201+
}
202+
203+
SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) {
204+
exists(Function f | f = this.getCallable() |
205+
exists(ParameterPosition pos | sc = Impl::Private::SummaryComponent::parameter(pos) |
206+
result = pos.getParameter(f)
207+
)
208+
or
209+
exists(ReturnKind rk |
210+
sc = Impl::Private::SummaryComponent::return(rk) and
211+
isReturnExpr(f, rk, result)
212+
)
213+
)
214+
}
215+
}
216+
217+
bindingset[source, sc]
218+
SourceSinkReportingElement getASourceReportingElement(
219+
Input::SourceBase source, Impl::Private::SummaryComponent sc
220+
) {
221+
exists(Call call | call.getTarget() = source |
222+
sc = Impl::Private::SummaryComponent::return(_) and
223+
result = call
224+
or
225+
exists(ArgumentPosition pos |
226+
sc = Impl::Private::SummaryComponent::argument(pos) and
227+
result = pos.getArgument(call)
228+
)
229+
)
230+
or
231+
exists(ParameterPosition pos |
232+
sc = Impl::Private::SummaryComponent::parameter(pos) and
233+
result = pos.getParameter(source)
234+
)
235+
}
236+
237+
pragma[nomagic]
238+
private IndirectReturnOutNode getIndirectReturn(CallInstruction call, NormalReturnKind rk) {
239+
result.getCallInstruction() = call and
240+
pragma[only_bind_out](result.getIndirectionIndex()) =
241+
pragma[only_bind_out](rk.getIndirectionIndex())
242+
}
243+
244+
pragma[nomagic]
245+
private predicate hasKindAndEnclosingFunction(Function f, ReturnKind rk, ReturnNode r) {
246+
r.getEnclosingCallable().asSourceCallable() = f and
247+
r.getKind() = rk
248+
}
142249

143-
DataFlowCallable getEnclosingCallable() { none() }
250+
pragma[nomagic]
251+
private predicate hasParameterAndIndirectionIndex(
252+
Parameter p, int indirectionIndex, ParameterNode n
253+
) {
254+
n.getParameter() = p and
255+
n.getIndirectionIndex() = indirectionIndex
256+
}
144257

145-
SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) { none() }
258+
bindingset[e, sc]
259+
Node getSourceDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
260+
exists(DataFlowCall call |
261+
exists(ArgumentPosition pos |
262+
sc = Impl::Private::SummaryComponent::argument(pos) and
263+
pos.getArgument(call.asCallInstruction().getUnconvertedResultExpression()) = e
264+
|
265+
pos.getIndirectionIndex() = 0 and
266+
result.(PostUpdateNode).getPreUpdateNode().asExpr() = e
267+
or
268+
result.(PostUpdateNode).getPreUpdateNode().asIndirectExpr(pos.getIndirectionIndex()) = e
269+
)
270+
or
271+
exists(ReturnKind rk |
272+
sc = Impl::Private::SummaryComponent::return(rk) and
273+
// When `e` is a call the node becomes an `OutNode`.
274+
e = call.asCallInstruction().getUnconvertedResultExpression()
275+
|
276+
rk.getIndirectionIndex() = 0 and
277+
simpleOutNode(result, call.asCallInstruction())
278+
or
279+
result = getIndirectReturn(call.asCallInstruction(), rk)
280+
)
281+
)
282+
or
283+
exists(ParameterPosition pos |
284+
sc = Impl::Private::SummaryComponent::parameter(pos) and
285+
hasParameterAndIndirectionIndex(e, pos.getIndirectionIndex(), result)
286+
)
287+
or
288+
exists(Function f, ReturnKind rk |
289+
sc = Impl::Private::SummaryComponent::return(rk) and
290+
// When `e` is the returned expression from a function the node is
291+
// the `ReturnNode`.
292+
isReturnExpr(f, rk, e) and
293+
hasKindAndEnclosingFunction(f, rk, result)
294+
)
295+
}
296+
297+
bindingset[sink, sc]
298+
SourceSinkReportingElement getASinkReportingElement(
299+
Input::SinkBase sink, Impl::Private::SummaryComponent sc
300+
) {
301+
exists(Call call, ArgumentPosition pos |
302+
call.getTarget() = sink and
303+
sc = Impl::Private::SummaryComponent::argument(pos) and
304+
result = pos.getArgument(call)
305+
)
306+
}
307+
308+
bindingset[e, sc]
309+
Node getSinkDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
310+
exists(ArgumentPosition pos, CallInstruction call |
311+
sc = Impl::Private::SummaryComponent::argument(pos) and
312+
pos.getArgument(call.getUnconvertedResultExpression()) = e and
313+
result.(ArgumentNode).sourceArgumentOf(call, pos)
314+
)
315+
or
316+
exists(Function f, ReturnKind rk |
317+
sc = Impl::Private::SummaryComponent::return(rk) and
318+
isReturnExpr(f, rk, e) and
319+
hasKindAndEnclosingFunction(f, rk, result)
320+
)
146321
}
147322
}
148323

@@ -319,3 +494,41 @@ module Private {
319494
}
320495

321496
module Public = Impl::Public;
497+
498+
private class SourceModelFunction extends Public::SourceElement instanceof Function {
499+
private string namespace;
500+
private string type;
501+
private boolean subtypes;
502+
private string name;
503+
private string signature;
504+
private string ext;
505+
506+
SourceModelFunction() {
507+
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
508+
this = interpretElement(namespace, type, subtypes, name, signature, ext)
509+
}
510+
511+
override predicate isSource(
512+
string output, string kind, Public::Provenance provenance, string model
513+
) {
514+
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model)
515+
}
516+
}
517+
518+
private class SinkModelFunction extends Public::SinkElement instanceof Function {
519+
private string namespace;
520+
private string type;
521+
private boolean subtypes;
522+
private string name;
523+
private string signature;
524+
private string ext;
525+
526+
SinkModelFunction() {
527+
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
528+
this = interpretElement(namespace, type, subtypes, name, signature, ext)
529+
}
530+
531+
override predicate isSink(string input, string kind, Public::Provenance provenance, string model) {
532+
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, model)
533+
}
534+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
private import cpp
77
private import DataFlowImplSpecific
88
private import TaintTrackingImplSpecific
9+
private import DataFlowNodes as Nodes
10+
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
911
private import codeql.dataflow.internal.DataFlowImplConsistency
1012

1113
private module Input implements InputSig<Location, CppDataFlow> {
@@ -14,6 +16,12 @@ private module Input implements InputSig<Location, CppDataFlow> {
1416
// complex to model here.
1517
any()
1618
}
19+
20+
predicate postWithInFlowExclude(CppDataFlow::Node n) {
21+
n instanceof Nodes::FlowSummaryNode
22+
or
23+
FlowSummaryImpl::Private::Steps::summaryLocalStep(_, n, _, _)
24+
}
1725
}
1826

1927
module Consistency = MakeConsistency<Location, CppDataFlow, CppTaintTracking, Input>;

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

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,43 @@ 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.getTarget() = this.getSourceElement() and
1579+
[call.getAnArgument(), call.getQualifier()] = this.getSourceSinkReportingElement()
1580+
}
15441581
}
15451582

15461583
/**
@@ -1655,13 +1692,13 @@ abstract private class AbstractParameterNode extends Node {
16551692
* Holds if this node represents an implicit `this` parameter, if it exists.
16561693
*/
16571694
predicate isThis() { none() } // overridden by subclasses
1658-
}
16591695

1660-
abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
16611696
/** Gets the indirection index of this parameter node. */
1662-
abstract int getIndirectionIndex();
1697+
int getIndirectionIndex() { none() }
16631698
}
16641699

1700+
abstract private class AbstractIndirectParameterNode extends AbstractParameterNode { }
1701+
16651702
pragma[noinline]
16661703
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
16671704
IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex
@@ -1725,7 +1762,9 @@ private class IndirectInstructionParameterNode extends AbstractIndirectParameter
17251762
final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) }
17261763
}
17271764

1728-
abstract private class AbstractDirectParameterNode extends AbstractParameterNode { }
1765+
abstract private class AbstractDirectParameterNode extends AbstractParameterNode {
1766+
override int getIndirectionIndex() { result = 0 }
1767+
}
17291768

17301769
/**
17311770
* A non-indirect parameter node that is represented as an `Instruction`.
@@ -1796,6 +1835,8 @@ private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
17961835
}
17971836

17981837
override Parameter getParameter() { result = p }
1838+
1839+
final override int getIndirectionIndex() { result = 0 }
17991840
}
18001841

18011842
private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode,

0 commit comments

Comments
 (0)