Skip to content

Commit 7ec86b5

Browse files
committed
C++: AdjustedConfiguration should not extend the same dataflow configuration as FromGlobalVarTaintTrackingCfg as this causes multiple configurations to be in scope for dataflow.
1 parent 6c1ec6d commit 7ec86b5

File tree

4 files changed

+146
-10
lines changed

4 files changed

+146
-10
lines changed

cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import cpp
22
import semmle.code.cpp.security.Security
33
private import semmle.code.cpp.ir.dataflow.DataFlow
44
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
5-
private import semmle.code.cpp.ir.dataflow.DataFlow2
5+
private import semmle.code.cpp.ir.dataflow.DataFlow3
66
private import semmle.code.cpp.ir.IR
77
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
88
private import semmle.code.cpp.controlflow.IRGuards
99
private import semmle.code.cpp.models.interfaces.Taint
1010
private import semmle.code.cpp.models.interfaces.DataFlow
1111
private import semmle.code.cpp.ir.dataflow.TaintTracking
1212
private import semmle.code.cpp.ir.dataflow.TaintTracking2
13+
private import semmle.code.cpp.ir.dataflow.TaintTracking3
1314
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
1415

1516
/**
@@ -380,7 +381,7 @@ module TaintedWithPath {
380381
string toString() { result = "TaintTrackingConfiguration" }
381382
}
382383

383-
private class AdjustedConfiguration extends TaintTracking2::Configuration {
384+
private class AdjustedConfiguration extends TaintTracking3::Configuration {
384385
AdjustedConfiguration() { this = "AdjustedConfiguration" }
385386

386387
override predicate isSource(DataFlow::Node source) {
@@ -438,11 +439,11 @@ module TaintedWithPath {
438439
*/
439440

440441
private newtype TPathNode =
441-
TWrapPathNode(DataFlow2::PathNode n) or
442+
TWrapPathNode(DataFlow3::PathNode n) or
442443
// There's a single newtype constructor for both sources and sinks since
443444
// that makes it easiest to deal with the case where source = sink.
444445
TEndpointPathNode(Element e) {
445-
exists(AdjustedConfiguration cfg, DataFlow2::Node sourceNode, DataFlow2::Node sinkNode |
446+
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
446447
cfg.hasFlow(sourceNode, sinkNode)
447448
|
448449
sourceNode = getNodeForExpr(e) and
@@ -473,7 +474,7 @@ module TaintedWithPath {
473474
}
474475

475476
private class WrapPathNode extends PathNode, TWrapPathNode {
476-
DataFlow2::PathNode inner() { this = TWrapPathNode(result) }
477+
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
477478

478479
override string toString() { result = this.inner().toString() }
479480

@@ -510,25 +511,25 @@ module TaintedWithPath {
510511

511512
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
512513
query predicate edges(PathNode a, PathNode b) {
513-
DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
514+
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
514515
or
515516
// To avoid showing trivial-looking steps, we _replace_ the last node instead
516517
// of adding an edge out of it.
517518
exists(WrapPathNode sinkNode |
518-
DataFlow2::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
519+
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
519520
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
520521
)
521522
or
522523
// Same for the first node
523524
exists(WrapPathNode sourceNode |
524-
DataFlow2::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
525+
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
525526
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
526527
)
527528
or
528529
// Finally, handle the case where the path goes directly from a source to a
529530
// sink, meaning that they both need to be translated.
530531
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
531-
DataFlow2::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
532+
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
532533
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
533534
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
534535
)
@@ -550,7 +551,7 @@ module TaintedWithPath {
550551
* the computation.
551552
*/
552553
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
553-
exists(AdjustedConfiguration cfg, DataFlow2::Node flowSource, DataFlow2::Node flowSink |
554+
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
554555
source = sourceNode.(InitialPathNode).inner() and
555556
flowSource = getNodeForExpr(source) and
556557
cfg.hasFlow(flowSource, flowSink) and
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Provides a `TaintTracking3` module, which is a copy of the `TaintTracking`
3+
* module. Use this class when data-flow configurations or taint-tracking
4+
* configurations must depend on each other. Two classes extending
5+
* `DataFlow::Configuration` should never depend on each other, but one of them
6+
* should instead depend on a `DataFlow2::Configuration`, a
7+
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
8+
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
9+
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
10+
*
11+
* See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
12+
*/
13+
module TaintTracking3 {
14+
import semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl
15+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Provides an implementation of global (interprocedural) taint tracking.
3+
* This file re-exports the local (intraprocedural) taint-tracking analysis
4+
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
5+
* exposed through the `Configuration` class. For some languages, this file
6+
* exists in several identical copies, allowing queries to use multiple
7+
* `Configuration` classes that depend on each other without introducing
8+
* mutual recursion among those configurations.
9+
*/
10+
11+
import TaintTrackingParameter::Public
12+
private import TaintTrackingParameter::Private
13+
14+
/**
15+
* A configuration of interprocedural taint tracking analysis. This defines
16+
* sources, sinks, and any other configurable aspect of the analysis. Each
17+
* use of the taint tracking library must define its own unique extension of
18+
* this abstract class.
19+
*
20+
* A taint-tracking configuration is a special data flow configuration
21+
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
22+
* necessarily preserve values but are still relevant from a taint tracking
23+
* perspective. (For example, string concatenation, where one of the operands
24+
* is tainted.)
25+
*
26+
* To create a configuration, extend this class with a subclass whose
27+
* characteristic predicate is a unique singleton string. For example, write
28+
*
29+
* ```ql
30+
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
31+
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
32+
* // Override `isSource` and `isSink`.
33+
* // Optionally override `isSanitizer`.
34+
* // Optionally override `isSanitizerIn`.
35+
* // Optionally override `isSanitizerOut`.
36+
* // Optionally override `isSanitizerGuard`.
37+
* // Optionally override `isAdditionalTaintStep`.
38+
* }
39+
* ```
40+
*
41+
* Then, to query whether there is flow between some `source` and `sink`,
42+
* write
43+
*
44+
* ```ql
45+
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
46+
* ```
47+
*
48+
* Multiple configurations can coexist, but it is unsupported to depend on
49+
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
50+
* overridden predicates that define sources, sinks, or additional steps.
51+
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
52+
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
53+
*/
54+
abstract class Configuration extends DataFlow::Configuration {
55+
bindingset[this]
56+
Configuration() { any() }
57+
58+
/**
59+
* Holds if `source` is a relevant taint source.
60+
*
61+
* The smaller this predicate is, the faster `hasFlow()` will converge.
62+
*/
63+
// overridden to provide taint-tracking specific qldoc
64+
abstract override predicate isSource(DataFlow::Node source);
65+
66+
/**
67+
* Holds if `sink` is a relevant taint sink.
68+
*
69+
* The smaller this predicate is, the faster `hasFlow()` will converge.
70+
*/
71+
// overridden to provide taint-tracking specific qldoc
72+
abstract override predicate isSink(DataFlow::Node sink);
73+
74+
/** Holds if the node `node` is a taint sanitizer. */
75+
predicate isSanitizer(DataFlow::Node node) { none() }
76+
77+
final override predicate isBarrier(DataFlow::Node node) {
78+
isSanitizer(node) or
79+
defaultTaintSanitizer(node)
80+
}
81+
82+
/** Holds if taint propagation into `node` is prohibited. */
83+
predicate isSanitizerIn(DataFlow::Node node) { none() }
84+
85+
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
86+
87+
/** Holds if taint propagation out of `node` is prohibited. */
88+
predicate isSanitizerOut(DataFlow::Node node) { none() }
89+
90+
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
91+
92+
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
93+
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
94+
95+
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
96+
97+
/**
98+
* Holds if the additional taint propagation step from `node1` to `node2`
99+
* must be taken into account in the analysis.
100+
*/
101+
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
102+
103+
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
104+
isAdditionalTaintStep(node1, node2) or
105+
defaultAdditionalTaintStep(node1, node2)
106+
}
107+
108+
/**
109+
* Holds if taint may flow from `source` to `sink` for this configuration.
110+
*/
111+
// overridden to provide taint-tracking specific qldoc
112+
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
113+
super.hasFlow(source, sink)
114+
}
115+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
2+
3+
module Private {
4+
import semmle.code.cpp.ir.dataflow.DataFlow3::DataFlow3 as DataFlow
5+
}

0 commit comments

Comments
 (0)