mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge pull request #4766 from criemen/cleanup-flow-tests
C++: Cleanup data/taint flow tests
This commit is contained in:
71
cpp/ql/test/TestUtilities/dataflow/FlowTestCommon.qll
Normal file
71
cpp/ql/test/TestUtilities/dataflow/FlowTestCommon.qll
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Helper library for implementing data or taint flow inline expectation tests.
|
||||
* As long as data or taint flow configurations for IR and/or AST based data/taint flow
|
||||
* are in scope, provides inline expectations tests.
|
||||
* All sinks that have flow are annotated with `ast` (or respective `ir`) if there
|
||||
* is a unique source for the flow.
|
||||
* Otherwise, if there are multiple sources that can reach a given sink, the annotations
|
||||
* have the form `ast=lineno:column` (or `ir=lineno:column`).
|
||||
* If a sink is reachable through both AST and IR flow, the annotations have the form
|
||||
* `ast,ir` or `ast,ir=lineno:column`.
|
||||
* Intermediate steps from the source to the sink are not annotated.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IRDataFlow
|
||||
private import semmle.code.cpp.dataflow.DataFlow::DataFlow as ASTDataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class IRFlowTest extends InlineExpectationsTest {
|
||||
IRFlowTest() { this = "IRFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ir" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(IRDataFlow::Node source, IRDataFlow::Node sink, IRDataFlow::Configuration conf, int n |
|
||||
tag = "ir" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(IRDataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ASTFlowTest extends InlineExpectationsTest {
|
||||
ASTFlowTest() { this = "ASTFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ast" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(
|
||||
ASTDataFlow::Node source, ASTDataFlow::Node sink, ASTDataFlow::Configuration conf, int n
|
||||
|
|
||||
tag = "ast" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(ASTDataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
class TestBarrierGuard extends DataFlow::BarrierGuard {
|
||||
TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" }
|
||||
|
||||
override predicate checks(Expr checked, boolean isTrue) {
|
||||
checked = this.(FunctionCall).getArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class TestAllocationConfig extends DataFlow::Configuration {
|
||||
TestAllocationConfig() { this = "TestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
class TestBarrierGuard extends DataFlow::BarrierGuard {
|
||||
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
|
||||
|
||||
override predicate checksInstr(Instruction checked, boolean isTrue) {
|
||||
checked = this.(CallInstruction).getPositionalArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class TestAllocationConfig extends DataFlow::Configuration {
|
||||
TestAllocationConfig() { this = "TestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(GlobalOrNamespaceVariable var | var.getName().matches("flowTestGlobal%") |
|
||||
writesVariable(n1.asInstruction(), var) and
|
||||
var = n2.asVariable()
|
||||
or
|
||||
readsVariable(n2.asInstruction(), var) and
|
||||
var = n1.asVariable()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
}
|
||||
@@ -1,27 +1,109 @@
|
||||
import DataflowTestCommon
|
||||
import TestUtilities.dataflow.FlowTestCommon
|
||||
|
||||
class ASTDataFlowTest extends InlineExpectationsTest {
|
||||
ASTDataFlowTest() { this = "ASTDataFlowTest" }
|
||||
module ASTTest {
|
||||
private import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
override string getARelevantTag() { result = "ast" }
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
class TestBarrierGuard extends DataFlow::BarrierGuard {
|
||||
TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, TestAllocationConfig conf, int n |
|
||||
tag = "ast" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
override predicate checks(Expr checked, boolean isTrue) {
|
||||
checked = this.(FunctionCall).getArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class ASTTestAllocationConfig extends DataFlow::Configuration {
|
||||
ASTTestAllocationConfig() { this = "ASTTestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
|
||||
}
|
||||
}
|
||||
|
||||
module IRTest {
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
class TestBarrierGuard extends DataFlow::BarrierGuard {
|
||||
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
|
||||
|
||||
override predicate checksInstr(Instruction checked, boolean isTrue) {
|
||||
checked = this.(CallInstruction).getPositionalArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class IRTestAllocationConfig extends DataFlow::Configuration {
|
||||
IRTestAllocationConfig() { this = "IRTestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(GlobalOrNamespaceVariable var | var.getName().matches("flowTestGlobal%") |
|
||||
writesVariable(n1.asInstruction(), var) and
|
||||
var = n2.asVariable()
|
||||
or
|
||||
readsVariable(n2.asInstruction(), var) and
|
||||
var = n1.asVariable()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import IRDataflowTestCommon
|
||||
|
||||
class IRDataFlowTest extends InlineExpectationsTest {
|
||||
IRDataFlowTest() { this = "IRDataFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ir" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, TestAllocationConfig conf, int n |
|
||||
tag = "ir" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
private import semmle.code.cpp.dataflow.DataFlow
|
||||
private import DataFlow
|
||||
|
||||
class Conf extends Configuration {
|
||||
Conf() { this = "FieldFlowConf" }
|
||||
class ASTConf extends Configuration {
|
||||
ASTConf() { this = "ASTFieldFlowConf" }
|
||||
|
||||
override predicate isSource(Node src) {
|
||||
src.asExpr() instanceof NewExpr
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import DataFlow
|
||||
|
||||
class Conf extends Configuration {
|
||||
Conf() { this = "FieldFlowConf" }
|
||||
class IRConf extends Configuration {
|
||||
IRConf() { this = "IRFieldFlowConf" }
|
||||
|
||||
override predicate isSource(Node src) {
|
||||
src.asExpr() instanceof NewExpr
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | AST only |
|
||||
| A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | AST only |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:66:14:66:14 | c | AST only |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:75:14:75:14 | c | AST only |
|
||||
| A.cpp:98:12:98:18 | new | A.cpp:120:16:120:16 | a | AST only |
|
||||
| A.cpp:142:14:142:20 | new | A.cpp:153:16:153:16 | c | AST only |
|
||||
| A.cpp:159:12:159:18 | new | A.cpp:165:26:165:29 | head | AST only |
|
||||
| A.cpp:159:12:159:18 | new | A.cpp:169:15:169:18 | head | AST only |
|
||||
| B.cpp:6:15:6:24 | new | B.cpp:9:20:9:24 | elem1 | AST only |
|
||||
| B.cpp:15:15:15:27 | new | B.cpp:19:20:19:24 | elem2 | AST only |
|
||||
| D.cpp:28:15:28:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:35:15:35:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:42:15:42:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:49:15:49:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:56:15:56:24 | new | D.cpp:64:25:64:28 | elem | AST only |
|
||||
| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw | AST only |
|
||||
| E.cpp:29:24:29:29 | ref arg buffer | E.cpp:32:13:32:18 | buffer | AST only |
|
||||
| E.cpp:30:28:30:33 | ref arg buffer | E.cpp:21:18:21:23 | buffer | AST only |
|
||||
| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | IR only |
|
||||
| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | IR only |
|
||||
| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | IR only |
|
||||
| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | IR only |
|
||||
| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | IR only |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | IR only |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | IR only |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | IR only |
|
||||
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | IR only |
|
||||
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | AST only |
|
||||
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | AST only |
|
||||
| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | AST only |
|
||||
| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | AST only |
|
||||
| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | AST only |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | AST only |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | AST only |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | AST only |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only |
|
||||
| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only |
|
||||
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only |
|
||||
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | AST only |
|
||||
| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | AST only |
|
||||
| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | AST only |
|
||||
| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:47:61:55 | bufferLen | AST only |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:33:25:33:25 | a | AST only |
|
||||
| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | AST only |
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Nodes
|
||||
import IRConfiguration as IRConf
|
||||
import ASTConfiguration as ASTConf
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow as IR
|
||||
private import semmle.code.cpp.dataflow.DataFlow as AST
|
||||
|
||||
from Node source, Node sink, IRConf::Conf irConf, ASTConf::Conf astConf, string msg
|
||||
where
|
||||
irConf.hasFlow(source.asIR(), sink.asIR()) and
|
||||
not exists(AST::DataFlow::Node astSource, AST::DataFlow::Node astSink |
|
||||
astSource.asExpr() = source.asIR().asExpr() and
|
||||
astSink.asExpr() = sink.asIR().asExpr()
|
||||
|
|
||||
astConf.hasFlow(astSource, astSink)
|
||||
) and
|
||||
msg = "IR only"
|
||||
or
|
||||
astConf.hasFlow(source.asAST(), sink.asAST()) and
|
||||
not exists(IR::DataFlow::Node irSource, IR::DataFlow::Node irSink |
|
||||
irSource.asExpr() = source.asAST().asExpr() and
|
||||
irSink.asExpr() = sink.asAST().asExpr()
|
||||
|
|
||||
irConf.hasFlow(irSource, irSink)
|
||||
) and
|
||||
msg = "AST only"
|
||||
select source, sink, msg
|
||||
@@ -1,34 +1,9 @@
|
||||
/**
|
||||
* @kind problem
|
||||
*/
|
||||
import TestUtilities.dataflow.FlowTestCommon
|
||||
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import ASTConfiguration
|
||||
import cpp
|
||||
|
||||
class ASTFieldFlowTest extends InlineExpectationsTest {
|
||||
ASTFieldFlowTest() { this = "ASTFieldFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ast" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, Conf conf, int n |
|
||||
tag = "ast" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
module ASTTest {
|
||||
private import ASTConfiguration
|
||||
}
|
||||
|
||||
module IRTest {
|
||||
private import IRConfiguration
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import IRConfiguration
|
||||
import cpp
|
||||
|
||||
class IRFieldFlowTest extends InlineExpectationsTest {
|
||||
IRFieldFlowTest() { this = "IRFieldFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ir" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, Conf conf, int n |
|
||||
tag = "ir" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,6 @@ import IRConfiguration
|
||||
import cpp
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode src, DataFlow::PathNode sink, Conf conf
|
||||
from DataFlow::PathNode src, DataFlow::PathNode sink, IRConf conf
|
||||
where conf.hasFlowPath(src, sink)
|
||||
select sink, src, sink, sink + " flows from $@", src, src.toString()
|
||||
|
||||
@@ -7,6 +7,6 @@ import ASTConfiguration
|
||||
import cpp
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode src, DataFlow::PathNode sink, Conf conf
|
||||
from DataFlow::PathNode src, DataFlow::PathNode sink, ASTConf conf
|
||||
where conf.hasFlowPath(src, sink)
|
||||
select sink, src, sink, sink + " flows from $@", src, src.toString()
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class TestAllocationConfig extends TaintTracking::Configuration {
|
||||
TestAllocationConfig() { this = "TestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument()
|
||||
or
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument() and
|
||||
sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr
|
||||
)
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getSideEffectOperand() = sink.asOperand() and
|
||||
read.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().hasName("sink")
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class TestAllocationConfig extends TaintTracking::Configuration {
|
||||
TestAllocationConfig() { this = "TestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
|
||||
}
|
||||
}
|
||||
|
||||
class SetMemberFunction extends TaintFunction {
|
||||
SetMemberFunction() { this.hasName("setMember") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
class GetMemberFunction extends TaintFunction {
|
||||
GetMemberFunction() { this.hasName("getMember") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
class SetStringFunction extends TaintFunction {
|
||||
SetStringFunction() { this.hasName("setString") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
class GetStringFunction extends TaintFunction {
|
||||
GetStringFunction() { this.hasName("getString") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
@@ -420,30 +420,30 @@ void test_qualifiers()
|
||||
sink(a);
|
||||
sink(a.getMember());
|
||||
a.setMember(source());
|
||||
sink(a); // $ ast MISSING: ir
|
||||
sink(a.getMember()); // $ ast MISSING: ir
|
||||
sink(a); // $ ast,ir
|
||||
sink(a.getMember()); // $ ast,ir
|
||||
|
||||
sink(b);
|
||||
sink(b.getMember());
|
||||
b.member = source();
|
||||
sink(b); // $ ir MISSING: ast
|
||||
sink(b.member); // $ ast,ir
|
||||
sink(b.getMember());
|
||||
sink(b.getMember()); // $ ir MISSING: ast
|
||||
|
||||
c = new MyClass2(0);
|
||||
|
||||
sink(c);
|
||||
sink(c->getMember());
|
||||
c->setMember(source());
|
||||
sink(c); // $ ast MISSING: ir
|
||||
sink(c->getMember()); // $ ast MISSING: ir
|
||||
sink(c); // $ ast,ir
|
||||
sink(c->getMember()); // $ ast,ir
|
||||
|
||||
delete c;
|
||||
|
||||
sink(d);
|
||||
sink(d.getString());
|
||||
d.setString(strings::source());
|
||||
sink(d); // $ ast MISSING: ir
|
||||
sink(d); // $ ast,ir
|
||||
sink(d.getString()); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,105 @@
|
||||
import TaintTestCommon
|
||||
import TestUtilities.dataflow.FlowTestCommon
|
||||
|
||||
class ASTTaintTest extends InlineExpectationsTest {
|
||||
ASTTaintTest() { this = "ASTTaintTest" }
|
||||
module TaintModels {
|
||||
class SetMemberFunction extends TaintFunction {
|
||||
SetMemberFunction() { this.hasName("setMember") }
|
||||
|
||||
override string getARelevantTag() { result = "ast" }
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, TestAllocationConfig conf, int n |
|
||||
tag = "ast" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
class GetMemberFunction extends TaintFunction {
|
||||
GetMemberFunction() { this.hasName("getMember") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
class SetStringFunction extends TaintFunction {
|
||||
SetStringFunction() { this.hasName("setString") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
class GetStringFunction extends TaintFunction {
|
||||
GetStringFunction() { this.hasName("getString") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module ASTTest {
|
||||
private import semmle.code.cpp.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class ASTTestAllocationConfig extends TaintTracking::Configuration {
|
||||
ASTTestAllocationConfig() { this = "ASTTestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module IRTest {
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
class TestAllocationConfig extends TaintTracking::Configuration {
|
||||
TestAllocationConfig() { this = "TestAllocationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument()
|
||||
or
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument() and
|
||||
sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr
|
||||
)
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getSideEffectOperand() = sink.asOperand() and
|
||||
read.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().hasName("sink")
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import IRTaintTestCommon
|
||||
|
||||
class IRTaintFlowTest extends InlineExpectationsTest {
|
||||
IRTaintFlowTest() { this = "IRTaintFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ir" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, TestAllocationConfig conf, int n |
|
||||
tag = "ir" and
|
||||
conf.hasFlow(source, sink) and
|
||||
n = strictcount(DataFlow::Node otherSource | conf.hasFlow(otherSource, sink)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user