Skip to content

Commit 80a0027

Browse files
committed
C++: Shared TaintTrackingImpl for IR TaintTracking
1 parent 7702125 commit 80a0027

File tree

8 files changed

+333
-153
lines changed

8 files changed

+333
-153
lines changed

config/identical-files.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
"TaintTracking::Configuration Java/C++/C#": [
3030
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
3131
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
32+
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
33+
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
3234
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
3335
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
3436
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",

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

Lines changed: 11 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -6,166 +6,24 @@
66
* the information from the source is preserved at the sink. For example, taint
77
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
88
* 100` since we consider a single bit of information to be too little.
9+
*
10+
* To use global (interprocedural) taint tracking, extend the class
11+
* `TaintTracking::Configuration` as documented on that class. To use local
12+
* (intraprocedural) taint tracking between expressions, call
13+
* `TaintTracking::localExprTaint`. For more general cases of local taint
14+
* tracking, call `TaintTracking::localTaint` or
15+
* `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`.
916
*/
1017

1118
import semmle.code.cpp.ir.dataflow.DataFlow
1219
import semmle.code.cpp.ir.dataflow.DataFlow2
13-
private import semmle.code.cpp.ir.IR
1420

1521
module TaintTracking {
16-
/**
17-
* A configuration of interprocedural taint tracking analysis. This defines
18-
* sources, sinks, and any other configurable aspect of the analysis. Each
19-
* use of the taint tracking library must define its own unique extension of
20-
* this abstract class.
21-
*
22-
* A taint-tracking configuration is a special data flow configuration
23-
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
24-
* necessarily preserve values but are still relevant from a taint-tracking
25-
* perspective. (For example, string concatenation, where one of the operands
26-
* is tainted.)
27-
*
28-
* To create a configuration, extend this class with a subclass whose
29-
* characteristic predicate is a unique singleton string. For example, write
30-
*
31-
* ```
32-
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
33-
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
34-
* // Override `isSource` and `isSink`.
35-
* // Optionally override `isSanitizer`.
36-
* // Optionally override `isAdditionalTaintStep`.
37-
* }
38-
* ```
39-
*
40-
* Then, to query whether there is flow between some `source` and `sink`,
41-
* write
42-
*
43-
* ```
44-
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
45-
* ```
46-
*
47-
* Multiple configurations can coexist, but it is unsupported to depend on a
48-
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
49-
* overridden predicates that define sources, sinks, or additional steps.
50-
* Instead, the dependency should go to a `TaintTracking::Configuration2` or
51-
* a `DataFlow{2,3,4}::Configuration`.
52-
*/
53-
abstract class Configuration extends DataFlow::Configuration {
54-
bindingset[this]
55-
Configuration() { any() }
56-
57-
/** Holds if `source` is a taint source. */
58-
// overridden to provide taint-tracking specific qldoc
59-
abstract override predicate isSource(DataFlow::Node source);
60-
61-
/** Holds if `sink` is a taint sink. */
62-
// overridden to provide taint-tracking specific qldoc
63-
abstract override predicate isSink(DataFlow::Node sink);
64-
65-
/**
66-
* Holds if taint should not flow into `node`.
67-
*/
68-
predicate isSanitizer(DataFlow::Node node) { none() }
69-
70-
/**
71-
* Holds if the additional taint propagation step
72-
* from `source` to `target` must be taken into account in the analysis.
73-
* This step will only be followed if `target` is not in the `isSanitizer`
74-
* predicate.
75-
*/
76-
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }
77-
78-
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
79-
80-
final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
81-
this.isAdditionalTaintStep(source, target)
82-
or
83-
localTaintStep(source, target)
84-
}
85-
}
86-
87-
/**
88-
* A taint-tracking configuration that is backed by the `DataFlow2` library
89-
* instead of `DataFlow`. Use this class when taint-tracking configurations
90-
* or data-flow configurations must depend on each other.
91-
*
92-
* See `TaintTracking::Configuration` for the full documentation.
93-
*/
94-
abstract class Configuration2 extends DataFlow2::Configuration {
95-
bindingset[this]
96-
Configuration2() { any() }
97-
98-
/** Holds if `source` is a taint source. */
99-
// overridden to provide taint-tracking specific qldoc
100-
abstract override predicate isSource(DataFlow::Node source);
101-
102-
/** Holds if `sink` is a taint sink. */
103-
// overridden to provide taint-tracking specific qldoc
104-
abstract override predicate isSink(DataFlow::Node sink);
105-
106-
/**
107-
* Holds if taint should not flow into `node`.
108-
*/
109-
predicate isSanitizer(DataFlow::Node node) { none() }
110-
111-
/**
112-
* Holds if the additional taint propagation step
113-
* from `source` to `target` must be taken into account in the analysis.
114-
* This step will only be followed if `target` is not in the `isSanitizer`
115-
* predicate.
116-
*/
117-
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }
118-
119-
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
120-
121-
final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
122-
this.isAdditionalTaintStep(source, target)
123-
or
124-
localTaintStep(source, target)
125-
}
126-
}
127-
128-
/**
129-
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
130-
* (intra-procedural) step.
131-
*/
132-
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
133-
// Taint can flow into using ordinary data flow.
134-
DataFlow::localFlowStep(nodeFrom, nodeTo)
135-
or
136-
localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
137-
}
138-
139-
/**
140-
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
141-
* (intra-procedural) step.
142-
*/
143-
private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction nodeTo) {
144-
// Taint can flow through expressions that alter the value but preserve
145-
// more than one bit of it _or_ expressions that follow data through
146-
// pointer indirections.
147-
nodeTo.getAnOperand().getAnyDef() = nodeFrom and
148-
(
149-
nodeTo instanceof ArithmeticInstruction
150-
or
151-
nodeTo instanceof BitwiseInstruction
152-
or
153-
nodeTo instanceof PointerArithmeticInstruction
154-
or
155-
nodeTo instanceof FieldAddressInstruction
156-
or
157-
// The `CopyInstruction` case is also present in non-taint data flow, but
158-
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
159-
// from a definition of `myStruct` to a `myStruct.myField` expression.
160-
nodeTo instanceof CopyInstruction
161-
)
162-
or
163-
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
164-
}
22+
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
23+
private import semmle.code.cpp.ir.dataflow.TaintTracking2
16524

16625
/**
167-
* Holds if taint may propagate from `source` to `sink` in zero or more local
168-
* (intra-procedural) steps.
26+
* DEPRECATED: Use TaintTracking2::Configuration instead.
16927
*/
170-
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
28+
deprecated class Configuration2 = TaintTracking2::Configuration;
17129
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Provides a `TaintTracking2` 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 TaintTracking2 {
14+
import semmle.code.cpp.ir.dataflow.internal.tainttracking2.TaintTrackingImpl
15+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
private import semmle.code.cpp.ir.IR
2+
private import semmle.code.cpp.ir.dataflow.DataFlow
3+
4+
/**
5+
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
6+
* (intra-procedural) step.
7+
*/
8+
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
9+
DataFlow::localFlowStep(nodeFrom, nodeTo)
10+
or
11+
localAdditionalTaintStep(nodeFrom, nodeTo)
12+
}
13+
14+
/**
15+
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
16+
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
17+
* different objects.
18+
*/
19+
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
20+
// Taint can flow into using ordinary data flow.
21+
DataFlow::localFlowStep(nodeFrom, nodeTo)
22+
or
23+
localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
24+
}
25+
26+
/**
27+
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
28+
* (intra-procedural) step.
29+
*/
30+
private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction nodeTo) {
31+
// Taint can flow through expressions that alter the value but preserve
32+
// more than one bit of it _or_ expressions that follow data through
33+
// pointer indirections.
34+
nodeTo.getAnOperand().getAnyDef() = nodeFrom and
35+
(
36+
nodeTo instanceof ArithmeticInstruction
37+
or
38+
nodeTo instanceof BitwiseInstruction
39+
or
40+
nodeTo instanceof PointerArithmeticInstruction
41+
or
42+
nodeTo instanceof FieldAddressInstruction
43+
or
44+
// The `CopyInstruction` case is also present in non-taint data flow, but
45+
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
46+
// from a definition of `myStruct` to a `myStruct.myField` expression.
47+
nodeTo instanceof CopyInstruction
48+
)
49+
or
50+
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
51+
}
52+
53+
/**
54+
* Holds if taint may propagate from `source` to `sink` in zero or more local
55+
* (intra-procedural) steps.
56+
*/
57+
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
58+
59+
/**
60+
* Holds if the additional step from `src` to `sink` should be included in all
61+
* global taint flow configurations.
62+
*/
63+
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
64+
localAdditionalTaintStep(src, sink)
65+
}
66+
67+
/**
68+
* Holds if `node` should be a barrier in all global taint flow configurations
69+
* but not in local taint.
70+
*/
71+
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import TaintTrackingParameter::Public
2+
private import TaintTrackingParameter::Private
3+
4+
/**
5+
* A configuration of interprocedural taint tracking analysis. This defines
6+
* sources, sinks, and any other configurable aspect of the analysis. Each
7+
* use of the taint tracking library must define its own unique extension of
8+
* this abstract class.
9+
*
10+
* A taint-tracking configuration is a special data flow configuration
11+
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
12+
* necessarily preserve values but are still relevant from a taint tracking
13+
* perspective. (For example, string concatenation, where one of the operands
14+
* is tainted.)
15+
*
16+
* To create a configuration, extend this class with a subclass whose
17+
* characteristic predicate is a unique singleton string. For example, write
18+
*
19+
* ```
20+
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
21+
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
22+
* // Override `isSource` and `isSink`.
23+
* // Optionally override `isSanitizer`.
24+
* // Optionally override `isSanitizerIn`.
25+
* // Optionally override `isSanitizerOut`.
26+
* // Optionally override `isSanitizerGuard`.
27+
* // Optionally override `isAdditionalTaintStep`.
28+
* }
29+
* ```
30+
*
31+
* Then, to query whether there is flow between some `source` and `sink`,
32+
* write
33+
*
34+
* ```
35+
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
36+
* ```
37+
*
38+
* Multiple configurations can coexist, but it is unsupported to depend on
39+
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
40+
* overridden predicates that define sources, sinks, or additional steps.
41+
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
42+
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
43+
*/
44+
abstract class Configuration extends DataFlow::Configuration {
45+
bindingset[this]
46+
Configuration() { any() }
47+
48+
/**
49+
* Holds if `source` is a relevant taint source.
50+
*
51+
* The smaller this predicate is, the faster `hasFlow()` will converge.
52+
*/
53+
// overridden to provide taint-tracking specific qldoc
54+
abstract override predicate isSource(DataFlow::Node source);
55+
56+
/**
57+
* Holds if `sink` is a relevant taint sink.
58+
*
59+
* The smaller this predicate is, the faster `hasFlow()` will converge.
60+
*/
61+
// overridden to provide taint-tracking specific qldoc
62+
abstract override predicate isSink(DataFlow::Node sink);
63+
64+
/** Holds if the node `node` is a taint sanitizer. */
65+
predicate isSanitizer(DataFlow::Node node) { none() }
66+
67+
final override predicate isBarrier(DataFlow::Node node) {
68+
isSanitizer(node) or
69+
defaultTaintBarrier(node)
70+
}
71+
72+
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
73+
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
74+
75+
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
76+
isSanitizerEdge(node1, node2)
77+
}
78+
79+
/** Holds if data flow into `node` is prohibited. */
80+
predicate isSanitizerIn(DataFlow::Node node) { none() }
81+
82+
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
83+
84+
/** Holds if data flow out of `node` is prohibited. */
85+
predicate isSanitizerOut(DataFlow::Node node) { none() }
86+
87+
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
88+
89+
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
90+
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
91+
92+
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
93+
94+
/**
95+
* Holds if the additional taint propagation step from `node1` to `node2`
96+
* must be taken into account in the analysis.
97+
*/
98+
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
99+
100+
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
101+
isAdditionalTaintStep(node1, node2) or
102+
defaultAdditionalTaintStep(node1, node2)
103+
}
104+
105+
/**
106+
* Holds if taint may flow from `source` to `sink` for this configuration.
107+
*/
108+
// overridden to provide taint-tracking specific qldoc
109+
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
110+
super.hasFlow(source, sink)
111+
}
112+
}
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.DataFlow::DataFlow as DataFlow
5+
}

0 commit comments

Comments
 (0)