Skip to content

Commit 1142a79

Browse files
authored
Merge pull request #4766 from criemen/cleanup-flow-tests
C++: Cleanup data/taint flow tests
2 parents edbbc84 + feb0554 commit 1142a79

21 files changed

+291
-455
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Helper library for implementing data or taint flow inline expectation tests.
3+
* As long as data or taint flow configurations for IR and/or AST based data/taint flow
4+
* are in scope, provides inline expectations tests.
5+
* All sinks that have flow are annotated with `ast` (or respective `ir`) if there
6+
* is a unique source for the flow.
7+
* Otherwise, if there are multiple sources that can reach a given sink, the annotations
8+
* have the form `ast=lineno:column` (or `ir=lineno:column`).
9+
* If a sink is reachable through both AST and IR flow, the annotations have the form
10+
* `ast,ir` or `ast,ir=lineno:column`.
11+
* Intermediate steps from the source to the sink are not annotated.
12+
*/
13+
14+
import cpp
15+
private import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IRDataFlow
16+
private import semmle.code.cpp.dataflow.DataFlow::DataFlow as ASTDataFlow
17+
import TestUtilities.InlineExpectationsTest
18+
19+
class IRFlowTest extends InlineExpectationsTest {
20+
IRFlowTest() { this = "IRFlowTest" }
21+
22+
override string getARelevantTag() { result = "ir" }
23+
24+
override predicate hasActualResult(Location location, string element, string tag, string value) {
25+
exists(IRDataFlow::Node source, IRDataFlow::Node sink, IRDataFlow::Configuration conf, int n |
26+
tag = "ir" and
27+
conf.hasFlow(source, sink) and
28+
n = strictcount(IRDataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
29+
(
30+
n = 1 and value = ""
31+
or
32+
// If there is more than one source for this sink
33+
// we specify the source location explicitly.
34+
n > 1 and
35+
value =
36+
source.getLocation().getStartLine().toString() + ":" +
37+
source.getLocation().getStartColumn()
38+
) and
39+
location = sink.getLocation() and
40+
element = sink.toString()
41+
)
42+
}
43+
}
44+
45+
class ASTFlowTest extends InlineExpectationsTest {
46+
ASTFlowTest() { this = "ASTFlowTest" }
47+
48+
override string getARelevantTag() { result = "ast" }
49+
50+
override predicate hasActualResult(Location location, string element, string tag, string value) {
51+
exists(
52+
ASTDataFlow::Node source, ASTDataFlow::Node sink, ASTDataFlow::Configuration conf, int n
53+
|
54+
tag = "ast" and
55+
conf.hasFlow(source, sink) and
56+
n = strictcount(ASTDataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
57+
(
58+
n = 1 and value = ""
59+
or
60+
// If there is more than one source for this sink
61+
// we specify the source location explicitly.
62+
n > 1 and
63+
value =
64+
source.getLocation().getStartLine().toString() + ":" +
65+
source.getLocation().getStartColumn()
66+
) and
67+
location = sink.getLocation() and
68+
element = sink.toString()
69+
)
70+
}
71+
}

cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll

Lines changed: 0 additions & 46 deletions
This file was deleted.

cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,109 @@
1-
import DataflowTestCommon
1+
import TestUtilities.dataflow.FlowTestCommon
22

3-
class ASTDataFlowTest extends InlineExpectationsTest {
4-
ASTDataFlowTest() { this = "ASTDataFlowTest" }
3+
module ASTTest {
4+
private import semmle.code.cpp.dataflow.DataFlow
55

6-
override string getARelevantTag() { result = "ast" }
6+
/**
7+
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
8+
* S in `if (guarded(x)) S`.
9+
*/
10+
// This is tested in `BarrierGuard.cpp`.
11+
class TestBarrierGuard extends DataFlow::BarrierGuard {
12+
TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" }
713

8-
override predicate hasActualResult(Location location, string element, string tag, string value) {
9-
exists(DataFlow::Node source, DataFlow::Node sink, TestAllocationConfig conf, int n |
10-
tag = "ast" and
11-
conf.hasFlow(source, sink) and
12-
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
13-
(
14-
n = 1 and value = ""
14+
override predicate checks(Expr checked, boolean isTrue) {
15+
checked = this.(FunctionCall).getArgument(0) and
16+
isTrue = true
17+
}
18+
}
19+
20+
/** Common data flow configuration to be used by tests. */
21+
class ASTTestAllocationConfig extends DataFlow::Configuration {
22+
ASTTestAllocationConfig() { this = "ASTTestAllocationConfig" }
23+
24+
override predicate isSource(DataFlow::Node source) {
25+
source.asExpr().(FunctionCall).getTarget().getName() = "source"
26+
or
27+
source.asParameter().getName().matches("source%")
28+
or
29+
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
30+
or
31+
// Track uninitialized variables
32+
exists(source.asUninitialized())
33+
}
34+
35+
override predicate isSink(DataFlow::Node sink) {
36+
exists(FunctionCall call |
37+
call.getTarget().getName() = "sink" and
38+
sink.asExpr() = call.getAnArgument()
39+
)
40+
}
41+
42+
override predicate isBarrier(DataFlow::Node barrier) {
43+
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
44+
}
45+
46+
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
47+
}
48+
}
49+
50+
module IRTest {
51+
private import semmle.code.cpp.ir.dataflow.DataFlow
52+
private import semmle.code.cpp.ir.IR
53+
54+
/**
55+
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
56+
* S in `if (guarded(x)) S`.
57+
*/
58+
// This is tested in `BarrierGuard.cpp`.
59+
class TestBarrierGuard extends DataFlow::BarrierGuard {
60+
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
61+
62+
override predicate checksInstr(Instruction checked, boolean isTrue) {
63+
checked = this.(CallInstruction).getPositionalArgument(0) and
64+
isTrue = true
65+
}
66+
}
67+
68+
/** Common data flow configuration to be used by tests. */
69+
class IRTestAllocationConfig extends DataFlow::Configuration {
70+
IRTestAllocationConfig() { this = "IRTestAllocationConfig" }
71+
72+
override predicate isSource(DataFlow::Node source) {
73+
source.asExpr().(FunctionCall).getTarget().getName() = "source"
74+
or
75+
source.asParameter().getName().matches("source%")
76+
}
77+
78+
override predicate isSink(DataFlow::Node sink) {
79+
exists(FunctionCall call |
80+
call.getTarget().getName() = "sink" and
81+
sink.asExpr() = call.getAnArgument()
82+
)
83+
}
84+
85+
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
86+
exists(GlobalOrNamespaceVariable var | var.getName().matches("flowTestGlobal%") |
87+
writesVariable(n1.asInstruction(), var) and
88+
var = n2.asVariable()
1589
or
16-
// If there is more than one source for this sink
17-
// we specify the source location explicitly.
18-
n > 1 and
19-
value =
20-
source.getLocation().getStartLine().toString() + ":" +
21-
source.getLocation().getStartColumn()
22-
) and
23-
location = sink.getLocation() and
24-
element = sink.toString()
25-
)
90+
readsVariable(n2.asInstruction(), var) and
91+
var = n1.asVariable()
92+
)
93+
}
94+
95+
override predicate isBarrier(DataFlow::Node barrier) {
96+
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
97+
}
98+
99+
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
100+
}
101+
102+
private predicate readsVariable(LoadInstruction load, Variable var) {
103+
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
104+
}
105+
106+
private predicate writesVariable(StoreInstruction store, Variable var) {
107+
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
26108
}
27109
}

cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected

Whitespace-only changes.

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

Lines changed: 0 additions & 27 deletions
This file was deleted.

cpp/ql/test/library-tests/dataflow/fields/ASTConfiguration.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
private import semmle.code.cpp.dataflow.DataFlow
22
private import DataFlow
33

4-
class Conf extends Configuration {
5-
Conf() { this = "FieldFlowConf" }
4+
class ASTConf extends Configuration {
5+
ASTConf() { this = "ASTFieldFlowConf" }
66

77
override predicate isSource(Node src) {
88
src.asExpr() instanceof NewExpr

cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
private import semmle.code.cpp.ir.dataflow.DataFlow
22
private import DataFlow
33

4-
class Conf extends Configuration {
5-
Conf() { this = "FieldFlowConf" }
4+
class IRConf extends Configuration {
5+
IRConf() { this = "IRFieldFlowConf" }
66

77
override predicate isSource(Node src) {
88
src.asExpr() instanceof NewExpr

0 commit comments

Comments
 (0)