Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9a9ec7b

Browse files
author
Robert Marsh
committed
C++: add IR-based taint tracking library
1 parent 173ade1 commit 9a9ec7b

7 files changed

Lines changed: 258 additions & 30 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Provides classes for performing local (intra-procedural) and
3+
* global (inter-procedural) taint-tracking analyses.
4+
*
5+
* We define _taint propagation_ informally to mean that a substantial part of
6+
* the information from the source is preserved at the sink. For example, taint
7+
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
8+
* 100` since we consider a single bit of information to be too little.
9+
*/
10+
11+
import semmle.code.cpp.ir.dataflow.DataFlow
12+
import semmle.code.cpp.ir.dataflow.DataFlow2
13+
private import semmle.code.cpp.ir.IR
14+
15+
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+
// Taint can flow through expressions that alter the value but preserve
137+
// more than one bit of it _or_ expressions that follow data through
138+
// pointer indirections.
139+
nodeTo.getAnOperand().getDefinitionInstruction() = nodeFrom and
140+
(
141+
nodeTo instanceof ArithmeticInstruction
142+
or
143+
nodeTo instanceof BitwiseInstruction
144+
or
145+
nodeTo instanceof PointerArithmeticInstruction
146+
or
147+
nodeTo instanceof FieldAddressInstruction
148+
)
149+
}
150+
151+
/**
152+
* Holds if taint may propagate from `source` to `sink` in zero or more local
153+
* (intra-procedural) steps.
154+
*/
155+
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
156+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import cpp
2+
import semmle.code.cpp.ir.dataflow.TaintTracking
3+
4+
/** Common data flow configuration to be used by tests. */
5+
class TestAllocationConfig extends TaintTracking::Configuration {
6+
TestAllocationConfig() {
7+
this = "TestAllocationConfig"
8+
}
9+
10+
override predicate isSource(DataFlow::Node source) {
11+
source.asExpr().(FunctionCall).getTarget().getName() = "source"
12+
or
13+
source.asParameter().getName().matches("source%")
14+
or
15+
// Track uninitialized variables
16+
exists(source.asUninitialized())
17+
}
18+
19+
override predicate isSink(DataFlow::Node sink) {
20+
exists(FunctionCall call |
21+
call.getTarget().getName() = "sink" and
22+
sink.asExpr() = call.getAnArgument()
23+
)
24+
}
25+
26+
override predicate isSanitizer(DataFlow::Node barrier) {
27+
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
28+
}
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.TaintTracking
3+
4+
/** Common data flow configuration to be used by tests. */
5+
class TestAllocationConfig extends TaintTracking::Configuration {
6+
TestAllocationConfig() {
7+
this = "TestAllocationConfig"
8+
}
9+
10+
override predicate isSource(DataFlow::Node source) {
11+
source.asExpr().(FunctionCall).getTarget().getName() = "source"
12+
or
13+
source.asParameter().getName().matches("source%")
14+
or
15+
// Track uninitialized variables
16+
exists(source.asUninitialized())
17+
}
18+
19+
override predicate isSink(DataFlow::Node sink) {
20+
exists(FunctionCall call |
21+
call.getTarget().getName() = "sink" and
22+
sink.asExpr() = call.getAnArgument()
23+
)
24+
}
25+
26+
override predicate isSanitizer(DataFlow::Node barrier) {
27+
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
28+
}
29+
}

cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
int source();
2-
void sink(...);
2+
void sink(...) {};
33

44
void arithAssignments(int source1, int clean1) {
55
sink(clean1); // clean

cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,4 @@
1-
import cpp
2-
import semmle.code.cpp.dataflow.TaintTracking
3-
4-
/** Common data flow configuration to be used by tests. */
5-
class TestAllocationConfig extends TaintTracking::Configuration {
6-
TestAllocationConfig() {
7-
this = "TestAllocationConfig"
8-
}
9-
10-
override predicate isSource(DataFlow::Node source) {
11-
source.asExpr().(FunctionCall).getTarget().getName() = "source"
12-
or
13-
source.asParameter().getName().matches("source%")
14-
or
15-
// Track uninitialized variables
16-
exists(source.asUninitialized())
17-
}
18-
19-
override predicate isSink(DataFlow::Node sink) {
20-
exists(FunctionCall call |
21-
call.getTarget().getName() = "sink" and
22-
sink.asExpr() = call.getAnArgument()
23-
)
24-
}
25-
26-
override predicate isSanitizer(DataFlow::Node barrier) {
27-
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
28-
}
29-
}
1+
import TaintTestCommon
302

313
from DataFlow::Node sink, DataFlow::Node source, TestAllocationConfig cfg
324
where cfg.hasFlow(source, sink)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import cpp
2+
import TaintTestCommon as ASTCommon
3+
import IRTaintTestCommon as IRCommon
4+
import semmle.code.cpp.dataflow.DataFlow as ASTDataFlow
5+
import semmle.code.cpp.ir.dataflow.DataFlow as IRDataFlow
6+
7+
predicate astFlow(Location sourceLocation, Location sinkLocation) {
8+
exists(ASTDataFlow::DataFlow::Node source, ASTDataFlow::DataFlow::Node sink,
9+
ASTCommon::TestAllocationConfig cfg |
10+
cfg.hasFlow(source, sink) and
11+
sourceLocation = source.getLocation() and
12+
sinkLocation = sink.getLocation()
13+
)
14+
}
15+
16+
predicate irFlow(Location sourceLocation, Location sinkLocation) {
17+
exists(IRDataFlow::DataFlow::Node source, IRDataFlow::DataFlow::Node sink,
18+
IRCommon::TestAllocationConfig cfg |
19+
cfg.hasFlow(source, sink) and
20+
sourceLocation = source.getLocation() and
21+
sinkLocation = sink.getLocation()
22+
)
23+
}
24+
25+
from Location sourceLocation, Location sinkLocation, string note
26+
where
27+
(
28+
astFlow(sourceLocation, sinkLocation) and
29+
not irFlow(sourceLocation, sinkLocation) and
30+
note = "AST only"
31+
) or
32+
(
33+
irFlow(sourceLocation, sinkLocation) and
34+
not astFlow(sourceLocation, sinkLocation) and
35+
note = "IR only"
36+
)
37+
select sourceLocation.toString(), sinkLocation.toString(), note
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import IRTaintTestCommon
2+
3+
from DataFlow::Node sink, DataFlow::Node source, TestAllocationConfig cfg
4+
where cfg.hasFlow(source, sink)
5+
select sink, source

0 commit comments

Comments
 (0)