Merge pull request #4766 from criemen/cleanup-flow-tests

C++: Cleanup data/taint flow tests
This commit is contained in:
Mathias Vorreiter Pedersen
2020-12-03 10:10:39 +01:00
committed by GitHub
21 changed files with 294 additions and 458 deletions

View 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()
)
}
}

View File

@@ -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 }
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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()
)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()
)
}
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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")
}
}

View File

@@ -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()
}
}

View File

@@ -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
}

View File

@@ -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")
}
}
}

View File

@@ -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()
)
}
}