mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'master' of github.com:Semmle/ql into attribute
This commit is contained in:
@@ -25,6 +25,8 @@
|
||||
- The predicate `Variable.getAnAssignedValue()` now reports assignments to fields resulting from aggregate initialization (` = {...}`).
|
||||
- The predicate `TypeMention.toString()` has been simplified to always return the string "`type mention`". This may improve performance when using `Element.toString()` or its descendants.
|
||||
- The `semmle.code.cpp.security.TaintTracking` library now considers a pointer difference calculation as blocking taint flow.
|
||||
- The second copy of the interprocedural `TaintTracking` library has been renamed from `TaintTracking::Configuration2` to `TaintTracking2::Configuration`, and the old name is now deprecated. Import `semmle.code.cpp.dataflow.TaintTracking2` to access the new name.
|
||||
- Fixed the `LocalScopeVariableReachability.qll` library's handling of loops with an entry condition is both always true upon first entry, and where there is more than one control flow path through the loop condition. This change increases the accuracy of the `LocalScopeVariableReachability.qll` library and queries which depend on it.
|
||||
- The `semmle.code.cpp.models` library now models data flow through `std::swap`.
|
||||
- There is a new `Variable.isThreadLocal()` predicate. It can be used to tell whether a variable is `thread_local`.
|
||||
- Recursion through the `DataFlow` library is now always a compile error. Such recursion has been deprecated since release 1.16. If one `DataFlow::Configuration` needs to depend on the results of another, switch one of them to use one of the `DataFlow2` through `DataFlow4` libraries.
|
||||
|
||||
@@ -41,5 +41,6 @@
|
||||
- The new predicate `ConstructedGeneric.getAnnotatedTypeArgument()` gets the annotated type of a type argument
|
||||
- The new predicate `TypeParameterConstraints.getAnAnnotatedTypeConstraint()` gets a type constraint with type annotations
|
||||
* The new class `SuppressNullableWarningExpr` models suppress-nullable-warning expressions such as `x!`
|
||||
* The data-flow library (and taint-tracking library) now supports flow through fields. All existing configurations will have field-flow enabled by default, but it can be disabled by adding `override int fieldFlowBranchLimit() { result = 0 }` to the configuration class. Field assignments, `this.Foo = x`, object initializers, `new C() { Foo = x }`, and field initializers `int Foo = 0` are supported.
|
||||
|
||||
## Changes to autobuilder
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
removes false positives that arose from paths through impossible `toString()`
|
||||
calls.
|
||||
* The library `VCS.qll` and all queries that imported it have been removed.
|
||||
|
||||
* The second copy of the interprocedural `TaintTracking` library has been renamed from `TaintTracking::Configuration2` to `TaintTracking2::Configuration`, and the old name is now deprecated. Import `semmle.code.java.dataflow.TaintTracking2` to access the new name.
|
||||
|
||||
@@ -25,6 +25,17 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#": [
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
|
||||
],
|
||||
"IR Instruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
|
||||
@@ -206,7 +206,8 @@ class Element extends ElementBase {
|
||||
namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
exprconv(unresolveElement(result), underlyingElement(this)) or
|
||||
param_decl_bind(underlyingElement(this),_,unresolveElement(result))
|
||||
param_decl_bind(underlyingElement(this),_,unresolveElement(result)) or
|
||||
using_container(unresolveElement(result),underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets the closest `Element` enclosing this one. */
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl2
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl3
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl4
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,259 +9,14 @@
|
||||
*/
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
module TaintTracking {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
private import semmle.code.cpp.dataflow.TaintTracking2
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint-tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerEdge`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on a
|
||||
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking::Configuration2` or
|
||||
* a `DataFlow{2,3,4}::Configuration`.
|
||||
* DEPRECATED: Use TaintTracking2::Configuration instead.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/** Holds if `source` is a taint source. */
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/** Holds if `sink` is a taint sink. */
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/**
|
||||
* Holds if taint should not flow into `node`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/** Holds if data flow from `node1` to `node2` is prohibited. */
|
||||
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step
|
||||
* from `source` to `target` must be taken into account in the analysis.
|
||||
* This step will only be followed if `target` is not in the `isSanitizer`
|
||||
* predicate.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node source,
|
||||
DataFlow::Node target)
|
||||
{ none() }
|
||||
|
||||
final override
|
||||
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/** DEPRECATED: use `isSanitizerEdge` instead. */
|
||||
override deprecated
|
||||
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
final override
|
||||
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
|
||||
this.isAdditionalTaintStep(source, target)
|
||||
or
|
||||
localTaintStep(source, target)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that is backed by the `DataFlow2` library
|
||||
* instead of `DataFlow`. Use this class when taint-tracking configurations
|
||||
* or data-flow configurations must depend on each other.
|
||||
*
|
||||
* See `TaintTracking::Configuration` for the full documentation.
|
||||
*/
|
||||
abstract class Configuration2 extends DataFlow2::Configuration {
|
||||
bindingset[this]
|
||||
Configuration2() { any() }
|
||||
|
||||
/** Holds if `source` is a taint source. */
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/** Holds if `sink` is a taint sink. */
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/**
|
||||
* Holds if taint should not flow into `node`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/** Holds if data flow from `node1` to `node2` is prohibited. */
|
||||
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step
|
||||
* from `source` to `target` must be taken into account in the analysis.
|
||||
* This step will only be followed if `target` is not in the `isSanitizer`
|
||||
* predicate.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node source,
|
||||
DataFlow::Node target)
|
||||
{ none() }
|
||||
|
||||
final override
|
||||
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/** DEPRECATED: use `isSanitizerEdge` instead. */
|
||||
override deprecated
|
||||
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
final override
|
||||
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
|
||||
this.isAdditionalTaintStep(source, target)
|
||||
or
|
||||
localTaintStep(source, target)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Taint can flow into using ordinary data flow.
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Taint can flow through expressions that alter the value but preserve
|
||||
// more than one bit of it _or_ expressions that follow data through
|
||||
// pointer indirections.
|
||||
exists(Expr exprFrom, Expr exprTo |
|
||||
exprFrom = nodeFrom.asExpr() and
|
||||
exprTo = nodeTo.asExpr()
|
||||
|
|
||||
exprFrom = exprTo.getAChild() and
|
||||
not noParentExprFlow(exprFrom, exprTo) and
|
||||
not noFlowFromChildExpr(exprTo)
|
||||
or
|
||||
// Taint can flow from the `x` variable in `x++` to all subsequent
|
||||
// accesses to the unmodified `x` variable.
|
||||
//
|
||||
// `DataFlow` without taint specifies flow from `++x` and `x += 1` into the
|
||||
// variable `x` and thus into subsequent accesses because those expressions
|
||||
// compute the same value as `x`. This is not the case for `x++`, which
|
||||
// computes a different value, so we have to add that ourselves for taint
|
||||
// tracking. The flow from expression `x` into `x++` etc. is handled in the
|
||||
// case above.
|
||||
exprTo = DataFlow::getAnAccessToAssignedVariable(
|
||||
exprFrom.(PostfixCrementOperation)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Taint can flow through modeled functions
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) {
|
||||
localTaintStep*(source, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we do not propagate taint from `fromExpr` to `toExpr`
|
||||
* even though `toExpr` is the AST parent of `fromExpr`.
|
||||
*/
|
||||
private predicate noParentExprFlow(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = toExpr.(ConditionalExpr).getCondition()
|
||||
or
|
||||
fromExpr = toExpr.(CommaExpr).getLeftOperand()
|
||||
or
|
||||
fromExpr = toExpr.(AssignExpr).getLValue() // LHS of `=`
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we do not propagate taint from a child of `e` to `e` itself.
|
||||
*/
|
||||
private predicate noFlowFromChildExpr(Expr e) {
|
||||
e instanceof ComparisonOperation
|
||||
or
|
||||
e instanceof LogicalAndExpr
|
||||
or
|
||||
e instanceof LogicalOrExpr
|
||||
or
|
||||
e instanceof Call
|
||||
or
|
||||
e instanceof SizeofOperator
|
||||
or
|
||||
e instanceof AlignofOperator
|
||||
}
|
||||
|
||||
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasDataFlow(inModel, outModel)
|
||||
|
|
||||
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
||||
// memcpy(&dest_var, tainted_ptr, len)
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasTaintFlow(inModel, outModel)
|
||||
|
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
or
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
deprecated
|
||||
class Configuration2 = TaintTracking2::Configuration;
|
||||
}
|
||||
|
||||
12
cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll
Normal file
12
cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*
|
||||
* We define _taint propagation_ informally to mean that a substantial part of
|
||||
* the information from the source is preserved at the sink. For example, taint
|
||||
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
|
||||
* 100` since we consider a single bit of information to be too little.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -35,7 +35,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlowNoCtx(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -152,7 +152,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -209,7 +209,7 @@ private module ImplCommon {
|
||||
* through a value-preserving method.
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -106,12 +106,33 @@ class ExprNode extends Node, TExprNode {
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
override Location getLocation() {
|
||||
result = getExprLocationOverride(expr)
|
||||
or
|
||||
not exists(getExprLocationOverride(expr)) and
|
||||
result = expr.getLocation()
|
||||
}
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a location for `e` that's more accurate than `e.getLocation()`, if any.
|
||||
*/
|
||||
private Location getExprLocationOverride(Expr e) {
|
||||
// Base case: the parent has a better location than `e`.
|
||||
e.getLocation() instanceof UnknownExprLocation and
|
||||
result = e.getParent().getLocation() and
|
||||
not result instanceof UnknownLocation
|
||||
or
|
||||
// Recursive case: the parent has a location override that's better than what
|
||||
// `e` has.
|
||||
e.getLocation() instanceof UnknownExprLocation and
|
||||
result = getExprLocationOverride(e.getParent()) and
|
||||
not result instanceof UnknownLocation
|
||||
}
|
||||
|
||||
abstract class ParameterNode extends Node, TNode {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
@@ -422,8 +443,18 @@ private module ThisFlow {
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
cached
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Expr -> Expr
|
||||
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*
|
||||
* We define _taint propagation_ informally to mean that a substantial part of
|
||||
* the information from the source is preserved at the sink. For example, taint
|
||||
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
|
||||
* 100` since we consider a single bit of information to be too little.
|
||||
*/
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
private module DataFlow {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
DataFlow::localFlowStep(src, sink) or
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a barrier in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Taint can flow through expressions that alter the value but preserve
|
||||
// more than one bit of it _or_ expressions that follow data through
|
||||
// pointer indirections.
|
||||
exists(Expr exprFrom, Expr exprTo |
|
||||
exprFrom = nodeFrom.asExpr() and
|
||||
exprTo = nodeTo.asExpr()
|
||||
|
|
||||
exprFrom = exprTo.getAChild() and
|
||||
not noParentExprFlow(exprFrom, exprTo) and
|
||||
not noFlowFromChildExpr(exprTo)
|
||||
or
|
||||
// Taint can flow from the `x` variable in `x++` to all subsequent
|
||||
// accesses to the unmodified `x` variable.
|
||||
//
|
||||
// `DataFlow` without taint specifies flow from `++x` and `x += 1` into the
|
||||
// variable `x` and thus into subsequent accesses because those expressions
|
||||
// compute the same value as `x`. This is not the case for `x++`, which
|
||||
// computes a different value, so we have to add that ourselves for taint
|
||||
// tracking. The flow from expression `x` into `x++` etc. is handled in the
|
||||
// case above.
|
||||
exprTo = DataFlow::getAnAccessToAssignedVariable(
|
||||
exprFrom.(PostfixCrementOperation)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Taint can flow through modeled functions
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) {
|
||||
localTaintStep*(source, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we do not propagate taint from `fromExpr` to `toExpr`
|
||||
* even though `toExpr` is the AST parent of `fromExpr`.
|
||||
*/
|
||||
private predicate noParentExprFlow(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = toExpr.(ConditionalExpr).getCondition()
|
||||
or
|
||||
fromExpr = toExpr.(CommaExpr).getLeftOperand()
|
||||
or
|
||||
fromExpr = toExpr.(AssignExpr).getLValue() // LHS of `=`
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we do not propagate taint from a child of `e` to `e` itself.
|
||||
*/
|
||||
private predicate noFlowFromChildExpr(Expr e) {
|
||||
e instanceof ComparisonOperation
|
||||
or
|
||||
e instanceof LogicalAndExpr
|
||||
or
|
||||
e instanceof LogicalOrExpr
|
||||
or
|
||||
e instanceof Call
|
||||
or
|
||||
e instanceof SizeofOperator
|
||||
or
|
||||
e instanceof AlignofOperator
|
||||
or
|
||||
e instanceof ClassAggregateLiteral
|
||||
or
|
||||
e instanceof FieldAccess
|
||||
}
|
||||
|
||||
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasDataFlow(inModel, outModel)
|
||||
|
|
||||
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
||||
// memcpy(&dest_var, tainted_ptr, len)
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasTaintFlow(inModel, outModel)
|
||||
|
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
or
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.cpp.dataflow.DataFlow::DataFlow as DataFlow
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.cpp.dataflow.DataFlow2::DataFlow2 as DataFlow
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import semmle.code.cpp.exprs.Expr
|
||||
* A C/C++ builtin operation.
|
||||
*/
|
||||
abstract class BuiltInOperation extends Expr {
|
||||
override string getCanonicalQLClass() { result = "BuiltInOperation" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -234,16 +234,33 @@ class ClassAggregateLiteral extends AggregateLiteral {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ aggregate literal that initializes an array
|
||||
* A C/C++ aggregate literal that initializes an array or a GNU vector type.
|
||||
*/
|
||||
class ArrayAggregateLiteral extends AggregateLiteral {
|
||||
ArrayType arrayType;
|
||||
|
||||
ArrayAggregateLiteral() {
|
||||
arrayType = this.getUnspecifiedType()
|
||||
class ArrayOrVectorAggregateLiteral extends AggregateLiteral {
|
||||
ArrayOrVectorAggregateLiteral() {
|
||||
exists(DerivedType type |
|
||||
type = this.getUnspecifiedType() and
|
||||
(
|
||||
type instanceof ArrayType or
|
||||
type instanceof GNUVectorType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "ArrayAggregateLiteral" }
|
||||
/**
|
||||
* Gets the number of elements initialized by this initializer list, either explicitly with an
|
||||
* expression, or by implicit value initialization.
|
||||
*/
|
||||
int getArraySize() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the elements in the initializer list.
|
||||
*/
|
||||
Type getElementType() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression within the aggregate literal that is used to initialize
|
||||
@@ -262,7 +279,7 @@ class ArrayAggregateLiteral extends AggregateLiteral {
|
||||
bindingset[elementIndex]
|
||||
predicate isInitialized(int elementIndex) {
|
||||
elementIndex >= 0 and
|
||||
elementIndex < arrayType.getArraySize()
|
||||
elementIndex < getArraySize()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,3 +296,45 @@ class ArrayAggregateLiteral extends AggregateLiteral {
|
||||
not exists(getElementExpr(elementIndex))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ aggregate literal that initializes an array
|
||||
*/
|
||||
class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral {
|
||||
ArrayType arrayType;
|
||||
|
||||
ArrayAggregateLiteral() {
|
||||
arrayType = this.getUnspecifiedType()
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "ArrayAggregateLiteral" }
|
||||
|
||||
override int getArraySize() {
|
||||
result = arrayType.getArraySize()
|
||||
}
|
||||
|
||||
override Type getElementType() {
|
||||
result = arrayType.getBaseType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ aggregate literal that initializes a GNU vector type.
|
||||
*/
|
||||
class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral {
|
||||
GNUVectorType vectorType;
|
||||
|
||||
VectorAggregateLiteral() {
|
||||
vectorType = this.getUnspecifiedType()
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "VectorAggregateLiteral" }
|
||||
|
||||
override int getArraySize() {
|
||||
result = vectorType.getNumElements()
|
||||
}
|
||||
|
||||
override Type getElementType() {
|
||||
result = vectorType.getBaseType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,4 @@ import cpp
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -35,7 +35,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlowNoCtx(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -152,7 +152,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -209,7 +209,7 @@ private module ImplCommon {
|
||||
* through a value-preserving method.
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,16 @@ UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable()
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
nodeTo.(CopyInstruction).getSourceValue() = nodeFrom or
|
||||
nodeTo.(PhiInstruction).getAnOperand().getDef() = nodeFrom or
|
||||
// Treat all conversions as flow, even conversions between different numeric types.
|
||||
|
||||
@@ -57,6 +57,7 @@ private newtype TOpcode =
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TPhi() or
|
||||
TBuiltIn() or
|
||||
TVarArgsStart() or
|
||||
TVarArgsEnd() or
|
||||
TVarArg() or
|
||||
@@ -115,7 +116,7 @@ abstract class CatchOpcode extends Opcode {}
|
||||
|
||||
abstract class OpcodeWithCondition extends Opcode {}
|
||||
|
||||
abstract class BuiltInOpcode extends Opcode {}
|
||||
abstract class BuiltInOperationOpcode extends Opcode {}
|
||||
|
||||
abstract class SideEffectOpcode extends Opcode {}
|
||||
|
||||
@@ -204,10 +205,11 @@ module Opcode {
|
||||
class UnmodeledUse extends Opcode, TUnmodeledUse { override final string toString() { result = "UnmodeledUse" } }
|
||||
class AliasedDefinition extends Opcode, TAliasedDefinition { override final string toString() { result = "AliasedDefinition" } }
|
||||
class Phi extends Opcode, TPhi { override final string toString() { result = "Phi" } }
|
||||
class VarArgsStart extends BuiltInOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } }
|
||||
class VarArgsEnd extends BuiltInOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } }
|
||||
class VarArg extends BuiltInOpcode, TVarArg { override final string toString() { result = "VarArg" } }
|
||||
class VarArgCopy extends BuiltInOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } }
|
||||
class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { override final string toString() { result = "BuiltIn" } }
|
||||
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } }
|
||||
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } }
|
||||
class VarArg extends BuiltInOperationOpcode, TVarArg { override final string toString() { result = "VarArg" } }
|
||||
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } }
|
||||
class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect { override final string toString() { result = "CallSideEffect" } }
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect { override final string toString() { result = "CallReadSideEffect" } }
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode, TIndirectReadSideEffect { override final string toString() { result = "IndirectReadSideEffect" } }
|
||||
|
||||
@@ -71,7 +71,7 @@ module InstructionSanity {
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
@@ -1831,8 +1831,29 @@ class UnreachedInstruction extends Instruction {
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
*/
|
||||
class BuiltInInstruction extends Instruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof BuiltInOpcode
|
||||
class BuiltInOperationInstruction extends Instruction {
|
||||
Language::BuiltInOperation operation;
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() {
|
||||
result = operation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation that does not have a specific opcode. The
|
||||
* actual operation is specified by the `getBuiltInOperation()` predicate.
|
||||
*/
|
||||
class BuiltInInstruction extends BuiltInOperationInstruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof Opcode::BuiltIn
|
||||
}
|
||||
|
||||
override final string getImmediateString() {
|
||||
result = getBuiltInOperation().toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +325,10 @@ cached private module Cached {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached Type getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ module InstructionSanity {
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
@@ -1831,8 +1831,29 @@ class UnreachedInstruction extends Instruction {
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
*/
|
||||
class BuiltInInstruction extends Instruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof BuiltInOpcode
|
||||
class BuiltInOperationInstruction extends Instruction {
|
||||
Language::BuiltInOperation operation;
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() {
|
||||
result = operation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation that does not have a specific opcode. The
|
||||
* actual operation is specified by the `getBuiltInOperation()` predicate.
|
||||
*/
|
||||
class BuiltInInstruction extends BuiltInOperationInstruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof Opcode::BuiltIn
|
||||
}
|
||||
|
||||
override final string getImmediateString() {
|
||||
result = getBuiltInOperation().toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +250,12 @@ cached private module Cached {
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction).getInstructionBuiltInOperation(
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached Type getInstructionExceptionType(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction).getInstructionExceptionType(
|
||||
|
||||
@@ -274,7 +274,7 @@ newtype TTranslatedElement =
|
||||
exists(ClassAggregateLiteral initList |
|
||||
initList.getFieldExpr(_).getFullyConverted() = expr
|
||||
) or
|
||||
exists(ArrayAggregateLiteral initList |
|
||||
exists(ArrayOrVectorAggregateLiteral initList |
|
||||
initList.getElementExpr(_).getFullyConverted() = expr
|
||||
) or
|
||||
exists(ReturnStmt returnStmt |
|
||||
@@ -320,13 +320,13 @@ newtype TTranslatedElement =
|
||||
} or
|
||||
// The initialization of an array element via a member of an initializer list.
|
||||
TTranslatedExplicitElementInitialization(
|
||||
ArrayAggregateLiteral initList, int elementIndex) {
|
||||
ArrayOrVectorAggregateLiteral initList, int elementIndex) {
|
||||
not ignoreExpr(initList) and
|
||||
exists(initList.getElementExpr(elementIndex))
|
||||
} or
|
||||
// The value initialization of a range of array elements that were omitted
|
||||
// from an initializer list.
|
||||
TTranslatedElementValueInitialization(ArrayAggregateLiteral initList,
|
||||
TTranslatedElementValueInitialization(ArrayOrVectorAggregateLiteral initList,
|
||||
int elementIndex, int elementCount) {
|
||||
not ignoreExpr(initList) and
|
||||
isFirstValueInitializedElementInRange(initList, elementIndex) and
|
||||
@@ -412,12 +412,13 @@ newtype TTranslatedElement =
|
||||
* `initList`, the result is the total number of elements in the array being
|
||||
* initialized.
|
||||
*/
|
||||
private int getEndOfValueInitializedRange(ArrayAggregateLiteral initList, int afterElementIndex) {
|
||||
private int getEndOfValueInitializedRange(ArrayOrVectorAggregateLiteral initList,
|
||||
int afterElementIndex) {
|
||||
result = getNextExplicitlyInitializedElementAfter(initList, afterElementIndex)
|
||||
or
|
||||
isFirstValueInitializedElementInRange(initList, afterElementIndex) and
|
||||
not exists(getNextExplicitlyInitializedElementAfter(initList, afterElementIndex)) and
|
||||
result = initList.getUnspecifiedType().(ArrayType).getArraySize()
|
||||
result = initList.getArraySize()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +428,7 @@ private int getEndOfValueInitializedRange(ArrayAggregateLiteral initList, int af
|
||||
* `initList`.
|
||||
*/
|
||||
private int getNextExplicitlyInitializedElementAfter(
|
||||
ArrayAggregateLiteral initList, int afterElementIndex) {
|
||||
ArrayOrVectorAggregateLiteral initList, int afterElementIndex) {
|
||||
isFirstValueInitializedElementInRange(initList, afterElementIndex) and
|
||||
result = min(int i | exists(initList.getElementExpr(i)) and i > afterElementIndex)
|
||||
}
|
||||
@@ -437,7 +438,7 @@ private int getNextExplicitlyInitializedElementAfter(
|
||||
* range of one or more consecutive value-initialized elements in `initList`.
|
||||
*/
|
||||
private predicate isFirstValueInitializedElementInRange(
|
||||
ArrayAggregateLiteral initList, int elementIndex) {
|
||||
ArrayOrVectorAggregateLiteral initList, int elementIndex) {
|
||||
initList.isValueInitialized(elementIndex) and
|
||||
(
|
||||
elementIndex = 0 or
|
||||
@@ -634,6 +635,13 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `BuiltInInstruction`, gets the built-in operation.
|
||||
*/
|
||||
BuiltInOperation getInstructionBuiltInOperation(InstructionTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `CatchByTypeInstruction`,
|
||||
* gets the type of the exception to be caught.
|
||||
|
||||
@@ -612,10 +612,15 @@ class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an array access expression (e.g. `a[i]`). The array being accessed will either
|
||||
* be a prvalue of pointer type (possibly due to an implicit array-to-pointer conversion), or a
|
||||
* glvalue of a GNU vector type.
|
||||
*/
|
||||
class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
override ArrayExpr expr;
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = getBaseOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
@@ -650,8 +655,8 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getBaseOperand().getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
@@ -671,7 +676,7 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = max(getBaseOperand().getResultType().(PointerType).getBaseType().getSize())
|
||||
result = max(getResultType().getSize())
|
||||
}
|
||||
|
||||
private TranslatedExpr getBaseOperand() {
|
||||
@@ -2373,7 +2378,13 @@ class TranslatedReThrowExpr extends TranslatedThrowExpr {
|
||||
* The IR translation of a built-in operation (i.e. anything that extends
|
||||
* `BuiltInOperation`).
|
||||
*/
|
||||
abstract class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
override BuiltInOperation expr;
|
||||
|
||||
TranslatedBuiltInOperation() {
|
||||
not expr instanceof BuiltInOperationBuiltInAddressOf // Handled specially
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
@@ -2423,7 +2434,14 @@ abstract class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
abstract Opcode getOpcode();
|
||||
Opcode getOpcode() {
|
||||
result instanceof Opcode::BuiltIn
|
||||
}
|
||||
|
||||
override final BuiltInOperation getInstructionBuiltInOperation(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = expr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -134,7 +134,7 @@ class TranslatedClassListInitialization extends TranslatedListInitialization
|
||||
* initializer list.
|
||||
*/
|
||||
class TranslatedArrayListInitialization extends TranslatedListInitialization {
|
||||
override ArrayAggregateLiteral expr;
|
||||
override ArrayOrVectorAggregateLiteral expr;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
// The children are in initialization order
|
||||
@@ -366,6 +366,11 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
tag = ZeroPadStringElementAddressTag() and
|
||||
result = max(getElementType().getSize())
|
||||
}
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
exists(int startIndex |
|
||||
zeroInitRange(startIndex, _) and
|
||||
@@ -638,7 +643,7 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
|
||||
* an element of an initializer list.
|
||||
*/
|
||||
abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
ArrayAggregateLiteral initList;
|
||||
ArrayOrVectorAggregateLiteral initList;
|
||||
|
||||
override final string toString() {
|
||||
result = initList.toString() + "[" + getElementIndex().toString() + "]"
|
||||
@@ -691,6 +696,11 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
tag = getElementAddressTag() and
|
||||
result = max(getElementType().getSize())
|
||||
}
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = getElementIndexTag() and
|
||||
result = getElementIndex().toString()
|
||||
@@ -706,13 +716,12 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
result = InitializerElementIndexTag()
|
||||
}
|
||||
|
||||
final ArrayAggregateLiteral getInitList() {
|
||||
final ArrayOrVectorAggregateLiteral getInitList() {
|
||||
result = initList
|
||||
}
|
||||
|
||||
final Type getElementType() {
|
||||
result = initList.getUnspecifiedType().(ArrayType).
|
||||
getBaseType().getUnspecifiedType()
|
||||
result = initList.getElementType().getUnspecifiedType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ module InstructionSanity {
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
@@ -1831,8 +1831,29 @@ class UnreachedInstruction extends Instruction {
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
*/
|
||||
class BuiltInInstruction extends Instruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof BuiltInOpcode
|
||||
class BuiltInOperationInstruction extends Instruction {
|
||||
Language::BuiltInOperation operation;
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() {
|
||||
result = operation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation that does not have a specific opcode. The
|
||||
* actual operation is specified by the `getBuiltInOperation()` predicate.
|
||||
*/
|
||||
class BuiltInInstruction extends BuiltInOperationInstruction {
|
||||
BuiltInInstruction() {
|
||||
getOpcode() instanceof Opcode::BuiltIn
|
||||
}
|
||||
|
||||
override final string getImmediateString() {
|
||||
result = getBuiltInOperation().toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +325,10 @@ cached private module Cached {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached Type getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ class StaticVariable = Cpp::Variable;
|
||||
class Parameter = Cpp::Parameter;
|
||||
class Field = Cpp::Field;
|
||||
|
||||
class BuiltInOperation = Cpp::BuiltInOperation;
|
||||
|
||||
// TODO: Remove necessity for these.
|
||||
class Expr = Cpp::Expr;
|
||||
class Class = Cpp::Class; // Used for inheritance conversions
|
||||
|
||||
@@ -207,7 +207,7 @@ private predicate unknownSign(Instruction i) {
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
or
|
||||
i instanceof BuiltInInstruction
|
||||
i instanceof BuiltInOperationInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
|
||||
@@ -4,9 +4,13 @@ import semmle.code.cpp.stmts.Stmt
|
||||
/**
|
||||
* A C/C++ block statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the block from `{` to `}` in the following code:
|
||||
* ```
|
||||
* { int a; int b = 1; a = b; }
|
||||
* {
|
||||
* int a;
|
||||
* int b = 1;
|
||||
* a = b;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Block extends Stmt, @stmt_block {
|
||||
|
||||
@@ -216,7 +216,13 @@ abstract class ConditionalStmt extends ControlStructure {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ 'if' statement.
|
||||
* A C/C++ 'if' statement. For example, the `if` statement in the following
|
||||
* code:
|
||||
* ```
|
||||
* if (x == 1) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
override string getCanonicalQLClass() { result = "IfStmt" }
|
||||
@@ -295,7 +301,13 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ 'constexpr if' statement.
|
||||
* A C/C++ 'constexpr if' statement. For example, the `if constexpr` statement
|
||||
* in the following code:
|
||||
* ```
|
||||
* if constexpr (x) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||
|
||||
@@ -386,9 +398,11 @@ abstract class Loop extends ControlStructure {
|
||||
/**
|
||||
* A C/C++ 'while' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `while` statement in the following code:
|
||||
* ```
|
||||
* while (b) { f(); }
|
||||
* while (b) {
|
||||
* f();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class WhileStmt extends Loop, @stmt_while {
|
||||
@@ -468,9 +482,11 @@ abstract class JumpStmt extends Stmt, @jump {
|
||||
/**
|
||||
* A C/C++ 'goto' statement which jumps to a label.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `goto` statement in the following code:
|
||||
* ```
|
||||
* goto someLabel;
|
||||
* ...
|
||||
* somelabel:
|
||||
* ```
|
||||
*/
|
||||
class GotoStmt extends JumpStmt, @stmt_goto {
|
||||
@@ -525,7 +541,7 @@ class GotoStmt extends JumpStmt, @stmt_goto {
|
||||
* A 'goto' statement whose target is computed by a non-constant
|
||||
* expression (a non-standard extension to C/C++).
|
||||
*
|
||||
* For example,
|
||||
* For example, the `goto` statement in the following code:
|
||||
* ```
|
||||
* goto *ptr;
|
||||
* ```
|
||||
@@ -560,9 +576,12 @@ class ComputedGotoStmt extends Stmt, @stmt_assigned_goto {
|
||||
/**
|
||||
* A C/C++ 'continue' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `continue` statement in the following code:
|
||||
* ```
|
||||
* continue;
|
||||
* while (x) {
|
||||
* if (arr[x] < 0) continue;
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ContinueStmt extends JumpStmt, @stmt_continue {
|
||||
@@ -590,9 +609,12 @@ private Stmt getEnclosingContinuable(Stmt s) {
|
||||
/**
|
||||
* A C/C++ 'break' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `break` statement in the following code:
|
||||
* ```
|
||||
* break;
|
||||
* while (x) {
|
||||
* if (arr[x] == 0) break;
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class BreakStmt extends JumpStmt, @stmt_break {
|
||||
@@ -620,9 +642,11 @@ private Stmt getEnclosingBreakable(Stmt s) {
|
||||
/**
|
||||
* A C/C++ 'label' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `somelabel:` statement in the following code:
|
||||
* ```
|
||||
* someLabel:
|
||||
* goto someLabel;
|
||||
* ...
|
||||
* somelabel:
|
||||
* ```
|
||||
*/
|
||||
class LabelStmt extends Stmt, @stmt_label {
|
||||
@@ -643,7 +667,7 @@ class LabelStmt extends Stmt, @stmt_label {
|
||||
/**
|
||||
* A C/C++ 'return' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example:
|
||||
* ```
|
||||
* return 1+2;
|
||||
* ```
|
||||
@@ -696,7 +720,7 @@ class ReturnStmt extends Stmt, @stmt_return {
|
||||
/**
|
||||
* A C/C++ 'do' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `do` ... `while` in the following code:
|
||||
* ```
|
||||
* do {
|
||||
* x = x + 1;
|
||||
@@ -838,7 +862,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* This only represents "traditional" 'for' statements and not C++11
|
||||
* range-based 'for' statements or Objective C 'for-in' statements.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `for` statement in:
|
||||
* ```
|
||||
* for (i = 0; i < 10; i++) { j++; }
|
||||
* ```
|
||||
@@ -1061,13 +1085,15 @@ private predicate inForUpdate(Expr forUpdate, Expr child) {
|
||||
/**
|
||||
* A C/C++ 'switch case' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `case` and `default` statements in:
|
||||
* ```
|
||||
* switch (i)
|
||||
* {
|
||||
* case 5:
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* ...
|
||||
* default:
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class SwitchCase extends Stmt, @stmt_switch_case {
|
||||
@@ -1399,9 +1425,15 @@ class SwitchCase extends Stmt, @stmt_switch_case {
|
||||
/**
|
||||
* A C/C++ 'default case' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `default` statement in:
|
||||
* ```
|
||||
* switch (i)
|
||||
* {
|
||||
* case 5:
|
||||
* ...
|
||||
* default:
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class DefaultCase extends SwitchCase {
|
||||
@@ -1421,14 +1453,14 @@ class DefaultCase extends SwitchCase {
|
||||
/**
|
||||
* A C/C++ 'switch' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `switch` statement in:
|
||||
* ```
|
||||
* switch(i) {
|
||||
* case 1:
|
||||
* case 2:
|
||||
* break;
|
||||
* default:
|
||||
* break;
|
||||
* switch (i)
|
||||
* {
|
||||
* case 5:
|
||||
* ...
|
||||
* default:
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@@ -1575,7 +1607,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
|
||||
* enum color { RED, GREEN, BLUE };
|
||||
* enum color c;
|
||||
* ```
|
||||
* the 'switch' statement
|
||||
* the `switch` statement in:
|
||||
* ```
|
||||
* switch (c) {
|
||||
* case RED:
|
||||
@@ -1639,7 +1671,16 @@ class EnumSwitch extends SwitchStmt {
|
||||
* execution continues with the next `Handler`.
|
||||
*
|
||||
* This has no concrete representation in the source, but makes the
|
||||
* control flow graph easier to use.
|
||||
* control flow graph easier to use. For example in the following code:
|
||||
* ```
|
||||
* try
|
||||
* {
|
||||
* f();
|
||||
* } catch (std::exception &e) {
|
||||
* g();
|
||||
* }
|
||||
* there is a handler that's associated with the `catch` block and controls
|
||||
* entry to it.
|
||||
*/
|
||||
class Handler extends Stmt, @stmt_handler {
|
||||
|
||||
@@ -1694,9 +1735,13 @@ deprecated class FinallyEnd extends Stmt {
|
||||
/**
|
||||
* A C/C++ 'try' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `try` statement in the following code:
|
||||
* ```
|
||||
* try { f(); } catch (...) { g(); }
|
||||
* try {
|
||||
* f();
|
||||
* } catch(std::exception &e) {
|
||||
* g();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class TryStmt extends Stmt, @stmt_try_block {
|
||||
@@ -1766,7 +1811,7 @@ class TryStmt extends Stmt, @stmt_try_block {
|
||||
* A C++ 'function try' statement.
|
||||
*
|
||||
* This is a 'try' statement wrapped around an entire function body,
|
||||
* for example:
|
||||
* for example the `try` statement in the following code:
|
||||
* ```
|
||||
* void foo() try {
|
||||
* f();
|
||||
@@ -1784,7 +1829,17 @@ class FunctionTryStmt extends TryStmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* A 'catch block', from either C++'s `catch` or Objective C's `@catch`.
|
||||
* A 'catch block', for example the second and third blocks in the following
|
||||
* code:
|
||||
* ```
|
||||
* try {
|
||||
* f();
|
||||
* } catch(std::exception &e) {
|
||||
* g();
|
||||
* } catch(...) {
|
||||
* h();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class CatchBlock extends Block {
|
||||
|
||||
@@ -1807,7 +1862,16 @@ class CatchBlock extends Block {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ 'catch-any block', that is, `catch(...) {stmts}`.
|
||||
* A C++ 'catch-any block', for example the third block in the following code:
|
||||
* ```
|
||||
* try {
|
||||
* f();
|
||||
* } catch(std::exception &e) {
|
||||
* g();
|
||||
* } catch(...) {
|
||||
* h();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class CatchAnyBlock extends CatchBlock {
|
||||
CatchAnyBlock() {
|
||||
@@ -1828,8 +1892,17 @@ class MicrosoftTryStmt extends Stmt, @stmt_microsoft_try {
|
||||
}
|
||||
|
||||
/**
|
||||
* A structured exception handling 'try except' statement, that is,
|
||||
* a `__try __except` statement. This is a Microsoft C/C++ extension.
|
||||
* A structured exception handling 'try except' statement, for example the
|
||||
* `__try` statement in the following code:
|
||||
* ```
|
||||
* __try
|
||||
* {
|
||||
* f();
|
||||
* } __except(myExceptionFilter()) {
|
||||
* g()
|
||||
* }
|
||||
* ```
|
||||
* This is a Microsoft C/C++ extension.
|
||||
*/
|
||||
class MicrosoftTryExceptStmt extends MicrosoftTryStmt {
|
||||
MicrosoftTryExceptStmt() {
|
||||
@@ -1850,8 +1923,17 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* A structured exception handling 'try finally' statement, that is,
|
||||
* a `__try __finally` statement. This is a Microsoft C/C++ extension.
|
||||
* A structured exception handling 'try finally' statement, for example the
|
||||
* `__try` statement in the following code:
|
||||
* ```
|
||||
* __try
|
||||
* {
|
||||
* f();
|
||||
* } __finally {
|
||||
* g()
|
||||
* }
|
||||
* ```
|
||||
* This is a Microsoft C/C++ extension.
|
||||
*/
|
||||
class MicrosoftTryFinallyStmt extends MicrosoftTryStmt {
|
||||
MicrosoftTryFinallyStmt() {
|
||||
@@ -1869,7 +1951,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt {
|
||||
/**
|
||||
* A C/C++ 'declaration' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the following statement is a declaration statement:
|
||||
* ```
|
||||
* int i, j;
|
||||
* ```
|
||||
@@ -1950,7 +2032,7 @@ class DeclStmt extends Stmt, @stmt_decl {
|
||||
/**
|
||||
* A C/C++ 'empty' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the following statement is an empty statement:
|
||||
* ```
|
||||
* ;
|
||||
* ```
|
||||
@@ -1968,7 +2050,7 @@ class EmptyStmt extends Stmt, @stmt_empty {
|
||||
/**
|
||||
* A C/C++ 'asm' statement.
|
||||
*
|
||||
* For example,
|
||||
* For example, the `__asm__` statement in the following code:
|
||||
* ```
|
||||
* __asm__("movb %bh (%eax)");
|
||||
* ```
|
||||
@@ -1982,8 +2064,12 @@ class AsmStmt extends Stmt, @stmt_asm {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C99 statement which computes the size of a single dimension of
|
||||
* a variable length array.
|
||||
* A C99 statement which computes the size of a single dimension of a
|
||||
* variable length array. For example the variable length array dimension
|
||||
* (`x`) in the following code:
|
||||
* ```
|
||||
* int myArray[x];
|
||||
* ```
|
||||
*
|
||||
* Each `VlaDeclStmt` is preceded by one `VlaDimensionStmt` for each
|
||||
* variable length dimension of the array.
|
||||
@@ -1998,7 +2084,11 @@ class VlaDimensionStmt extends Stmt, @stmt_set_vla_size {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C99 statement which declares a variable length array.
|
||||
* A C99 statement which declares a variable length array. For example
|
||||
* the variable length array declaration in the following code:
|
||||
* ```
|
||||
* int myArray[x];
|
||||
* ```
|
||||
*
|
||||
* Each `VlaDeclStmt` is preceded by one `VlaDimensionStmt` for each
|
||||
* variable length dimension of the array.
|
||||
|
||||
@@ -146,7 +146,7 @@ void following_pointers(
|
||||
|
||||
twoIntFields sArray[1] = { { source(), source() } };
|
||||
// TODO: fix this like above
|
||||
sink(sArray[0].m2); // no flow (due to limitations of the analysis)
|
||||
sink(sArray[0].m2); // flow (AST dataflow misses this due to limitations of the analysis)
|
||||
|
||||
twoIntFields sSwapped = { .m2 = source(), .m1 = 0 };
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
| acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source |
|
||||
| file://:0:0:0:0 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| file://:0:0:0:0 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| file://:0:0:0:0 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:14:3:14:6 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:18:8:18:8 | call to operator() | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:21:3:21:6 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:29:3:29:6 | t | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:35:8:35:8 | a | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| lambdas.cpp:41:8:41:8 | a | lambdas.cpp:8:10:8:15 | call to source |
|
||||
| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| lambdas.cpp:8:10:8:15 | file://:0:0:0:0 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:14:3:14:6 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:18:8:18:8 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:21:3:21:6 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:29:3:29:6 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:35:8:35:8 | AST only |
|
||||
| lambdas.cpp:8:10:8:15 | lambdas.cpp:41:8:41:8 | AST only |
|
||||
| test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only |
|
||||
@@ -8,6 +10,7 @@
|
||||
| test.cpp:136:27:136:32 | test.cpp:137:27:137:28 | AST only |
|
||||
| test.cpp:136:27:136:32 | test.cpp:138:27:138:34 | AST only |
|
||||
| test.cpp:136:27:136:32 | test.cpp:140:22:140:23 | AST only |
|
||||
| test.cpp:147:42:147:47 | test.cpp:149:18:149:19 | IR only |
|
||||
| test.cpp:395:17:395:22 | test.cpp:397:10:397:18 | AST only |
|
||||
| test.cpp:407:13:407:18 | test.cpp:413:10:413:14 | AST only |
|
||||
| test.cpp:421:13:421:18 | test.cpp:417:10:417:14 | AST only |
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
| test.cpp:126:8:126:19 | Convert: (const int *)... | test.cpp:120:9:120:20 | InitializeParameter: sourceArray1 |
|
||||
| test.cpp:126:8:126:19 | Load: sourceArray1 | test.cpp:120:9:120:20 | InitializeParameter: sourceArray1 |
|
||||
| test.cpp:145:10:145:11 | Load: m2 | test.cpp:142:32:142:37 | Call: call to source |
|
||||
| test.cpp:149:18:149:19 | Load: m2 | test.cpp:147:42:147:47 | Call: call to source |
|
||||
| test.cpp:153:17:153:18 | Load: m2 | test.cpp:151:35:151:40 | Call: call to source |
|
||||
| test.cpp:188:8:188:8 | Load: y | test.cpp:186:27:186:32 | Call: call to source |
|
||||
| test.cpp:192:8:192:8 | Load: s | test.cpp:199:33:199:38 | Call: call to source |
|
||||
|
||||
@@ -84,8 +84,10 @@ edges
|
||||
| C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] | C.cpp:18:12:18:18 | call to C [s3, ... (1)] |
|
||||
| C.cpp:24:5:24:25 | ... = ... [void] | C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] |
|
||||
| C.cpp:24:16:24:25 | new [void] | C.cpp:24:5:24:25 | ... = ... [void] |
|
||||
| C.cpp:27:8:27:11 | `this` parameter in func [s1, ... (1)] | file://:0:0:0:0 | this [s1, ... (1)] |
|
||||
| C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] | file://:0:0:0:0 | this [s3, ... (1)] |
|
||||
| C.cpp:27:8:27:11 | `this` parameter in func [s1, ... (1)] | C.cpp:29:10:29:11 | this [s1, ... (1)] |
|
||||
| C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] | C.cpp:31:10:31:11 | this [s3, ... (1)] |
|
||||
| C.cpp:29:10:29:11 | this [s1, ... (1)] | C.cpp:29:10:29:11 | s1 |
|
||||
| C.cpp:31:10:31:11 | this [s3, ... (1)] | C.cpp:31:10:31:11 | s3 |
|
||||
| constructors.cpp:26:15:26:15 | f [a_, ... (1)] | constructors.cpp:28:10:28:10 | f [a_, ... (1)] |
|
||||
| constructors.cpp:26:15:26:15 | f [b_, ... (1)] | constructors.cpp:29:10:29:10 | f [b_, ... (1)] |
|
||||
| constructors.cpp:28:10:28:10 | f [a_, ... (1)] | constructors.cpp:28:12:28:12 | call to a |
|
||||
@@ -102,8 +104,6 @@ edges
|
||||
| constructors.cpp:43:9:43:9 | g [b_, ... (1)] | constructors.cpp:26:15:26:15 | f [b_, ... (1)] |
|
||||
| constructors.cpp:46:9:46:9 | h [a_, ... (1)] | constructors.cpp:26:15:26:15 | f [a_, ... (1)] |
|
||||
| constructors.cpp:46:9:46:9 | h [b_, ... (1)] | constructors.cpp:26:15:26:15 | f [b_, ... (1)] |
|
||||
| file://:0:0:0:0 | this [s1, ... (1)] | C.cpp:29:10:29:11 | s1 |
|
||||
| file://:0:0:0:0 | this [s3, ... (1)] | C.cpp:31:10:31:11 | s3 |
|
||||
| simple.cpp:26:15:26:15 | f [a_, ... (1)] | simple.cpp:28:10:28:10 | f [a_, ... (1)] |
|
||||
| simple.cpp:26:15:26:15 | f [b_, ... (1)] | simple.cpp:29:10:29:10 | f [b_, ... (1)] |
|
||||
| simple.cpp:28:10:28:10 | f [a_, ... (1)] | simple.cpp:28:12:28:12 | call to a |
|
||||
|
||||
@@ -3,6 +3,8 @@ import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.dataflow.TaintTracking2
|
||||
import semmle.code.cpp.dataflow.RecursionPrevention
|
||||
|
||||
class TestConf1 extends DataFlow::Configuration {
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
| file://:0:0:0:0 | t | taint.cpp:235:11:239:2 | {...} | TAINT |
|
||||
| file://:0:0:0:0 | t | taint.cpp:243:11:246:2 | {...} | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | t | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | t | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | t | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | t | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | u | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | u | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | u | TAINT |
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | v | TAINT |
|
||||
| file://:0:0:0:0 | this | taint.cpp:72:3:72:3 | c | TAINT |
|
||||
| file://:0:0:0:0 | this | taint.cpp:73:3:73:3 | d | TAINT |
|
||||
| file://:0:0:0:0 | this | taint.cpp:77:3:77:3 | d | TAINT |
|
||||
| file://:0:0:0:0 | u | taint.cpp:235:11:239:2 | {...} | TAINT |
|
||||
| file://:0:0:0:0 | u | taint.cpp:243:11:246:2 | {...} | TAINT |
|
||||
| file://:0:0:0:0 | v | taint.cpp:235:11:239:2 | {...} | TAINT |
|
||||
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
|
||||
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
|
||||
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
|
||||
@@ -66,13 +44,14 @@
|
||||
| taint.cpp:71:14:71:17 | 0 | taint.cpp:71:14:71:17 | constructor init of field a | TAINT |
|
||||
| taint.cpp:71:14:71:17 | constructor init of field a [post-this] | taint.cpp:71:20:71:30 | constructor init of field b [pre-this] | |
|
||||
| taint.cpp:71:14:71:17 | constructor init of field a [pre-this] | taint.cpp:71:20:71:30 | constructor init of field b [pre-this] | |
|
||||
| taint.cpp:71:20:71:30 | constructor init of field b [post-this] | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:71:20:71:30 | constructor init of field b [pre-this] | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:71:20:71:30 | constructor init of field b [post-this] | taint.cpp:72:3:72:3 | this | |
|
||||
| taint.cpp:71:20:71:30 | constructor init of field b [pre-this] | taint.cpp:72:3:72:3 | this | |
|
||||
| taint.cpp:71:22:71:27 | call to source | taint.cpp:71:20:71:30 | constructor init of field b | TAINT |
|
||||
| taint.cpp:72:3:72:3 | this [post update] | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:72:3:72:3 | this | taint.cpp:73:3:73:3 | this | |
|
||||
| taint.cpp:72:3:72:3 | this [post update] | taint.cpp:73:3:73:3 | this | |
|
||||
| taint.cpp:72:7:72:12 | call to source | taint.cpp:72:3:72:14 | ... = ... | |
|
||||
| taint.cpp:73:7:73:7 | 0 | taint.cpp:73:3:73:7 | ... = ... | |
|
||||
| taint.cpp:76:7:76:14 | `this` parameter in myMethod | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:76:7:76:14 | `this` parameter in myMethod | taint.cpp:77:3:77:3 | this | |
|
||||
| taint.cpp:77:7:77:12 | call to source | taint.cpp:77:3:77:14 | ... = ... | |
|
||||
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:86:2:86:4 | mc1 | |
|
||||
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:88:7:88:9 | mc1 | |
|
||||
@@ -87,14 +66,6 @@
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:89:7:89:9 | mc1 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:90:7:90:9 | mc1 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:91:7:91:9 | mc1 | |
|
||||
| taint.cpp:88:7:88:9 | mc1 | taint.cpp:88:11:88:11 | a | TAINT |
|
||||
| taint.cpp:89:7:89:9 | mc1 | taint.cpp:89:11:89:11 | b | TAINT |
|
||||
| taint.cpp:90:7:90:9 | mc1 | taint.cpp:90:11:90:11 | c | TAINT |
|
||||
| taint.cpp:91:7:91:9 | mc1 | taint.cpp:91:11:91:11 | d | TAINT |
|
||||
| taint.cpp:92:7:92:9 | mc2 | taint.cpp:92:11:92:11 | a | TAINT |
|
||||
| taint.cpp:93:7:93:9 | mc2 | taint.cpp:93:11:93:11 | b | TAINT |
|
||||
| taint.cpp:94:7:94:9 | mc2 | taint.cpp:94:11:94:11 | c | TAINT |
|
||||
| taint.cpp:95:7:95:9 | mc2 | taint.cpp:95:11:95:11 | d | TAINT |
|
||||
| taint.cpp:100:21:100:21 | i | taint.cpp:106:7:106:7 | i | |
|
||||
| taint.cpp:100:21:100:21 | i | taint.cpp:110:12:110:12 | i | |
|
||||
| taint.cpp:100:21:100:21 | i | taint.cpp:112:12:112:12 | i | |
|
||||
@@ -200,17 +171,17 @@
|
||||
| taint.cpp:213:12:213:12 | x | taint.cpp:213:15:213:15 | ref arg y | |
|
||||
| taint.cpp:213:15:213:15 | ref arg y | taint.cpp:216:7:216:7 | y | |
|
||||
| taint.cpp:213:15:213:15 | y | taint.cpp:213:12:213:12 | ref arg x | |
|
||||
| taint.cpp:223:10:223:15 | call to source | file://:0:0:0:0 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | file://:0:0:0:0 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | taint.cpp:228:12:228:12 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | taint.cpp:235:11:239:2 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | taint.cpp:243:11:246:2 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | taint.cpp:253:4:253:4 | t | |
|
||||
| taint.cpp:223:10:223:15 | call to source | taint.cpp:260:4:260:4 | t | |
|
||||
| taint.cpp:224:9:224:10 | 0 | file://:0:0:0:0 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | file://:0:0:0:0 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | taint.cpp:228:15:228:15 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | taint.cpp:235:11:239:2 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | taint.cpp:243:11:246:2 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | taint.cpp:253:7:253:7 | u | |
|
||||
| taint.cpp:224:9:224:10 | 0 | taint.cpp:260:7:260:7 | u | |
|
||||
| taint.cpp:225:9:225:10 | 0 | file://:0:0:0:0 | v | |
|
||||
| taint.cpp:225:9:225:10 | 0 | taint.cpp:235:11:239:2 | v | |
|
||||
| taint.cpp:225:9:225:10 | 0 | taint.cpp:241:7:241:7 | v | |
|
||||
| taint.cpp:226:9:226:10 | 0 | taint.cpp:260:10:260:10 | w | |
|
||||
| taint.cpp:226:9:226:10 | 0 | taint.cpp:261:7:261:7 | w | |
|
||||
@@ -220,10 +191,10 @@
|
||||
| taint.cpp:228:11:228:11 | `this` parameter in (constructor) | taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | |
|
||||
| taint.cpp:228:11:232:2 | [...](...){...} | taint.cpp:233:7:233:7 | a | |
|
||||
| taint.cpp:228:11:232:2 | {...} | taint.cpp:228:11:232:2 | [...](...){...} | |
|
||||
| taint.cpp:228:12:228:12 | t | taint.cpp:228:11:232:2 | {...} | TAINT |
|
||||
| taint.cpp:228:15:228:15 | u | taint.cpp:228:11:232:2 | {...} | TAINT |
|
||||
| taint.cpp:228:17:228:17 | `this` parameter in operator() | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:228:17:228:17 | `this` parameter in operator() | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:228:17:228:17 | `this` parameter in operator() | taint.cpp:229:3:229:6 | this | |
|
||||
| taint.cpp:228:17:228:17 | `this` parameter in operator() | taint.cpp:244:3:244:6 | this | |
|
||||
| taint.cpp:229:3:229:6 | this | taint.cpp:230:3:230:6 | this | |
|
||||
| taint.cpp:230:3:230:6 | this | taint.cpp:231:3:231:11 | this | |
|
||||
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field t | TAINT |
|
||||
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field u | TAINT |
|
||||
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field v | TAINT |
|
||||
@@ -234,7 +205,9 @@
|
||||
| taint.cpp:235:11:235:11 | constructor init of field u [pre-this] | taint.cpp:235:11:235:11 | constructor init of field v [pre-this] | |
|
||||
| taint.cpp:235:11:239:2 | [...](...){...} | taint.cpp:240:2:240:2 | b | |
|
||||
| taint.cpp:235:11:239:2 | {...} | taint.cpp:235:11:239:2 | [...](...){...} | |
|
||||
| taint.cpp:235:15:235:15 | `this` parameter in operator() | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:235:15:235:15 | `this` parameter in operator() | taint.cpp:236:3:236:6 | this | |
|
||||
| taint.cpp:236:3:236:6 | this | taint.cpp:237:3:237:6 | this | |
|
||||
| taint.cpp:237:3:237:6 | this | taint.cpp:238:3:238:14 | this | |
|
||||
| taint.cpp:238:7:238:12 | call to source | taint.cpp:238:3:238:14 | ... = ... | |
|
||||
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field t | TAINT |
|
||||
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field u | TAINT |
|
||||
@@ -242,8 +215,9 @@
|
||||
| taint.cpp:243:11:243:11 | `this` parameter in (constructor) | taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | |
|
||||
| taint.cpp:243:11:246:2 | [...](...){...} | taint.cpp:247:2:247:2 | c | |
|
||||
| taint.cpp:243:11:246:2 | {...} | taint.cpp:243:11:246:2 | [...](...){...} | |
|
||||
| taint.cpp:243:15:243:15 | `this` parameter in operator() | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:243:15:243:15 | `this` parameter in operator() | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:243:15:243:15 | `this` parameter in operator() | taint.cpp:229:3:229:6 | this | |
|
||||
| taint.cpp:243:15:243:15 | `this` parameter in operator() | taint.cpp:244:3:244:6 | this | |
|
||||
| taint.cpp:244:3:244:6 | this | taint.cpp:245:3:245:6 | this | |
|
||||
| taint.cpp:249:11:252:2 | [...](...){...} | taint.cpp:253:2:253:2 | d | |
|
||||
| taint.cpp:249:18:249:18 | a | taint.cpp:250:8:250:8 | a | |
|
||||
| taint.cpp:249:25:249:25 | b | taint.cpp:251:8:251:8 | b | |
|
||||
|
||||
@@ -106,7 +106,7 @@ void array_test(int i) {
|
||||
arr2[i] = source();
|
||||
arr3[5] = 0;
|
||||
|
||||
sink(arr1[5]); // tainted [NOT DETECTED]
|
||||
sink(arr1[5]); // tainted
|
||||
sink(arr1[i]); // tainted [NOT DETECTED]
|
||||
sink(arr2[5]); // tainted [NOT DETECTED]
|
||||
sink(arr2[i]); // tainted [NOT DETECTED]
|
||||
@@ -227,14 +227,14 @@ void test_lambdas()
|
||||
|
||||
auto a = [t, u]() -> int {
|
||||
sink(t); // tainted
|
||||
sink(u); // clean [FALSE POSITIVE]
|
||||
sink(u); // clean
|
||||
return t;
|
||||
};
|
||||
sink(a()); // tainted
|
||||
|
||||
auto b = [&] {
|
||||
sink(t); // tainted
|
||||
sink(u); // clean [FALSE POSITIVE]
|
||||
sink(u); // clean
|
||||
v = source(); // (v is reference captured)
|
||||
};
|
||||
b();
|
||||
@@ -242,7 +242,7 @@ void test_lambdas()
|
||||
|
||||
auto c = [=] {
|
||||
sink(t); // tainted
|
||||
sink(u); // clean [FALSE POSITIVE]
|
||||
sink(u); // clean
|
||||
};
|
||||
c();
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
| file://:0:0:0:0 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| file://:0:0:0:0 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| file://:0:0:0:0 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| file://:0:0:0:0 | u | taint.cpp:223:10:223:15 | call to source |
|
||||
| file://:0:0:0:0 | u | taint.cpp:223:10:223:15 | call to source |
|
||||
| file://:0:0:0:0 | u | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
|
||||
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
|
||||
@@ -28,6 +22,9 @@
|
||||
| taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source |
|
||||
| taint.cpp:215:7:215:7 | x | taint.cpp:207:6:207:11 | call to source |
|
||||
| taint.cpp:216:7:216:7 | y | taint.cpp:207:6:207:11 | call to source |
|
||||
| taint.cpp:229:3:229:6 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:233:8:233:8 | call to operator() | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:236:3:236:6 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:244:3:244:6 | t | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:250:8:250:8 | a | taint.cpp:223:10:223:15 | call to source |
|
||||
| taint.cpp:256:8:256:8 | a | taint.cpp:223:10:223:15 | call to source |
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
| file://:0:0:0:0 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
|
||||
@@ -7,6 +6,7 @@
|
||||
| taint.cpp:91:11:91:11 | taint.cpp:77:7:77:12 | AST only |
|
||||
| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only |
|
||||
| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only |
|
||||
| taint.cpp:130:7:130:9 | taint.cpp:127:8:127:13 | IR only |
|
||||
| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
|
||||
| taint.cpp:173:8:173:13 | taint.cpp:164:19:164:24 | AST only |
|
||||
@@ -15,6 +15,9 @@
|
||||
| taint.cpp:195:7:195:7 | taint.cpp:193:6:193:6 | AST only |
|
||||
| taint.cpp:215:7:215:7 | taint.cpp:207:6:207:11 | AST only |
|
||||
| taint.cpp:216:7:216:7 | taint.cpp:207:6:207:11 | AST only |
|
||||
| taint.cpp:229:3:229:6 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:233:8:233:8 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:236:3:236:6 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:244:3:244:6 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:250:8:250:8 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:256:8:256:8 | taint.cpp:223:10:223:15 | AST only |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
| taint.cpp:8:8:8:13 | Load: clean1 | taint.cpp:4:27:4:33 | InitializeParameter: source1 |
|
||||
| taint.cpp:16:8:16:14 | Load: source1 | taint.cpp:12:22:12:27 | Call: call to source |
|
||||
| taint.cpp:17:8:17:16 | Add: ++ ... | taint.cpp:12:22:12:27 | Call: call to source |
|
||||
| taint.cpp:109:7:109:13 | Load: access to array | taint.cpp:105:12:105:17 | Call: call to source |
|
||||
| taint.cpp:129:7:129:9 | Load: * ... | taint.cpp:120:11:120:16 | Call: call to source |
|
||||
| taint.cpp:130:7:130:9 | Load: * ... | taint.cpp:127:8:127:13 | Call: call to source |
|
||||
| taint.cpp:134:7:134:9 | Load: * ... | taint.cpp:120:11:120:16 | Call: call to source |
|
||||
|
||||
@@ -8005,6 +8005,117 @@ ir.cpp:
|
||||
# 1147| 2: [Handler] <handler>
|
||||
# 1147| 0: [CatchBlock] { ... }
|
||||
# 1149| 1: [ReturnStmt] return ...
|
||||
# 1153| [TopLevelFunction] void VectorTypes(int)
|
||||
# 1153| params:
|
||||
# 1153| 0: [Parameter] i
|
||||
# 1153| Type = [IntType] int
|
||||
# 1153| body: [Block] { ... }
|
||||
# 1154| 0: [DeclStmt] declaration
|
||||
# 1154| 0: [VariableDeclarationEntry] definition of vi4
|
||||
# 1154| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1154| init: [Initializer] initializer for vi4
|
||||
# 1154| expr: [VectorAggregateLiteral] {...}
|
||||
# 1154| Type = [GNUVectorType] __attribute((vector_size(16))) int
|
||||
# 1154| ValueCategory = prvalue
|
||||
# 1154| 0: [Literal] 0
|
||||
# 1154| Type = [IntType] int
|
||||
# 1154| Value = [Literal] 0
|
||||
# 1154| ValueCategory = prvalue
|
||||
# 1154| 1: [Literal] 1
|
||||
# 1154| Type = [IntType] int
|
||||
# 1154| Value = [Literal] 1
|
||||
# 1154| ValueCategory = prvalue
|
||||
# 1154| 2: [Literal] 2
|
||||
# 1154| Type = [IntType] int
|
||||
# 1154| Value = [Literal] 2
|
||||
# 1154| ValueCategory = prvalue
|
||||
# 1154| 3: [Literal] 3
|
||||
# 1154| Type = [IntType] int
|
||||
# 1154| Value = [Literal] 3
|
||||
# 1154| ValueCategory = prvalue
|
||||
# 1155| 1: [DeclStmt] declaration
|
||||
# 1155| 0: [VariableDeclarationEntry] definition of x
|
||||
# 1155| Type = [IntType] int
|
||||
# 1155| init: [Initializer] initializer for x
|
||||
# 1155| expr: [ArrayExpr] access to array
|
||||
# 1155| Type = [IntType] int
|
||||
# 1155| ValueCategory = prvalue(load)
|
||||
# 1155| 0: [VariableAccess] vi4
|
||||
# 1155| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1155| ValueCategory = lvalue
|
||||
# 1155| 1: [VariableAccess] i
|
||||
# 1155| Type = [IntType] int
|
||||
# 1155| ValueCategory = prvalue(load)
|
||||
# 1156| 2: [ExprStmt] ExprStmt
|
||||
# 1156| 0: [AssignExpr] ... = ...
|
||||
# 1156| Type = [IntType] int
|
||||
# 1156| ValueCategory = lvalue
|
||||
# 1156| 0: [ArrayExpr] access to array
|
||||
# 1156| Type = [IntType] int
|
||||
# 1156| ValueCategory = lvalue
|
||||
# 1156| 0: [VariableAccess] vi4
|
||||
# 1156| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1156| ValueCategory = lvalue
|
||||
# 1156| 1: [VariableAccess] i
|
||||
# 1156| Type = [IntType] int
|
||||
# 1156| ValueCategory = prvalue(load)
|
||||
# 1156| 1: [VariableAccess] x
|
||||
# 1156| Type = [IntType] int
|
||||
# 1156| ValueCategory = prvalue(load)
|
||||
# 1157| 3: [DeclStmt] declaration
|
||||
# 1157| 0: [VariableDeclarationEntry] definition of vi4_shuffle
|
||||
# 1157| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1157| init: [Initializer] initializer for vi4_shuffle
|
||||
# 1157| expr: [BuiltInOperationBuiltInShuffleVector] __builtin_shufflevector
|
||||
# 1157| Type = [GNUVectorType] __attribute((vector_size(16))) int
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1157| 0: [VariableAccess] vi4
|
||||
# 1157| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1157| ValueCategory = prvalue(load)
|
||||
# 1157| 1: [VariableAccess] vi4
|
||||
# 1157| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1157| ValueCategory = prvalue(load)
|
||||
#-----| 2: [AddExpr] ... + ...
|
||||
#-----| Type = [IntType] int
|
||||
#-----| Value = [AddExpr] 3
|
||||
#-----| ValueCategory = prvalue
|
||||
# 1157| 0: [Literal] 3
|
||||
# 1157| Type = [IntType] int
|
||||
# 1157| Value = [Literal] 3
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1157| 1: [Literal] 0
|
||||
# 1157| Type = [IntType] int
|
||||
# 1157| Value = [Literal] 0
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1157| 3: [Literal] 2
|
||||
# 1157| Type = [IntType] int
|
||||
# 1157| Value = [Literal] 2
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1157| 4: [Literal] 1
|
||||
# 1157| Type = [IntType] int
|
||||
# 1157| Value = [Literal] 1
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1157| 5: [Literal] 0
|
||||
# 1157| Type = [IntType] int
|
||||
# 1157| Value = [Literal] 0
|
||||
# 1157| ValueCategory = prvalue
|
||||
# 1158| 4: [ExprStmt] ExprStmt
|
||||
# 1158| 0: [AssignExpr] ... = ...
|
||||
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1158| ValueCategory = lvalue
|
||||
# 1158| 0: [VariableAccess] vi4
|
||||
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1158| ValueCategory = lvalue
|
||||
# 1158| 1: [AddExpr] ... + ...
|
||||
# 1158| Type = [GNUVectorType] __attribute((vector_size(16))) int
|
||||
# 1158| ValueCategory = prvalue
|
||||
# 1158| 0: [VariableAccess] vi4
|
||||
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1158| ValueCategory = prvalue(load)
|
||||
# 1158| 1: [VariableAccess] vi4_shuffle
|
||||
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
|
||||
# 1158| ValueCategory = prvalue(load)
|
||||
# 1159| 5: [ReturnStmt] return ...
|
||||
perf-regression.cpp:
|
||||
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
||||
# 4| params:
|
||||
|
||||
@@ -1148,4 +1148,14 @@ void TryCatchNoCatchAny(bool b) {
|
||||
}
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17
|
||||
#define vector(elcount, type) __attribute__((vector_size((elcount)*sizeof(type)))) type
|
||||
|
||||
void VectorTypes(int i) {
|
||||
vector(4, int) vi4 = { 0, 1, 2, 3 };
|
||||
int x = vi4[i];
|
||||
vi4[i] = x;
|
||||
vector(4, int) vi4_shuffle = __builtin_shufflevector(vi4, vi4, 3+0, 2, 1, 0);
|
||||
vi4 = vi4 + vi4_shuffle;
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17 --clang
|
||||
|
||||
@@ -784,7 +784,7 @@ ir.cpp:
|
||||
# 174| r0_10(int *) = Load : &:r0_9, ~mu0_2
|
||||
# 174| r0_11(glval<int>) = VariableAddress[i] :
|
||||
# 174| r0_12(int) = Load : &:r0_11, ~mu0_2
|
||||
# 174| r0_13(int *) = PointerAdd[4] : r0_10, r0_12
|
||||
# 174| r0_13(glval<int>) = PointerAdd[4] : r0_10, r0_12
|
||||
# 174| r0_14(int) = Load : &:r0_13, ~mu0_2
|
||||
# 174| r0_15(glval<int>) = VariableAddress[x] :
|
||||
# 174| mu0_16(int) = Store : &:r0_15, r0_14
|
||||
@@ -792,7 +792,7 @@ ir.cpp:
|
||||
# 175| r0_18(int *) = Load : &:r0_17, ~mu0_2
|
||||
# 175| r0_19(glval<int>) = VariableAddress[i] :
|
||||
# 175| r0_20(int) = Load : &:r0_19, ~mu0_2
|
||||
# 175| r0_21(int *) = PointerAdd[4] : r0_18, r0_20
|
||||
# 175| r0_21(glval<int>) = PointerAdd[4] : r0_18, r0_20
|
||||
# 175| r0_22(int) = Load : &:r0_21, ~mu0_2
|
||||
# 175| r0_23(glval<int>) = VariableAddress[x] :
|
||||
# 175| mu0_24(int) = Store : &:r0_23, r0_22
|
||||
@@ -802,7 +802,7 @@ ir.cpp:
|
||||
# 177| r0_28(int *) = Load : &:r0_27, ~mu0_2
|
||||
# 177| r0_29(glval<int>) = VariableAddress[i] :
|
||||
# 177| r0_30(int) = Load : &:r0_29, ~mu0_2
|
||||
# 177| r0_31(int *) = PointerAdd[4] : r0_28, r0_30
|
||||
# 177| r0_31(glval<int>) = PointerAdd[4] : r0_28, r0_30
|
||||
# 177| mu0_32(int) = Store : &:r0_31, r0_26
|
||||
# 178| r0_33(glval<int>) = VariableAddress[x] :
|
||||
# 178| r0_34(int) = Load : &:r0_33, ~mu0_2
|
||||
@@ -810,7 +810,7 @@ ir.cpp:
|
||||
# 178| r0_36(int *) = Load : &:r0_35, ~mu0_2
|
||||
# 178| r0_37(glval<int>) = VariableAddress[i] :
|
||||
# 178| r0_38(int) = Load : &:r0_37, ~mu0_2
|
||||
# 178| r0_39(int *) = PointerAdd[4] : r0_36, r0_38
|
||||
# 178| r0_39(glval<int>) = PointerAdd[4] : r0_36, r0_38
|
||||
# 178| mu0_40(int) = Store : &:r0_39, r0_34
|
||||
# 180| r0_41(glval<int[10]>) = VariableAddress[a] :
|
||||
# 180| mu0_42(int[10]) = Uninitialized[a] : &:r0_41
|
||||
@@ -818,7 +818,7 @@ ir.cpp:
|
||||
# 181| r0_44(int *) = Convert : r0_43
|
||||
# 181| r0_45(glval<int>) = VariableAddress[i] :
|
||||
# 181| r0_46(int) = Load : &:r0_45, ~mu0_2
|
||||
# 181| r0_47(int *) = PointerAdd[4] : r0_44, r0_46
|
||||
# 181| r0_47(glval<int>) = PointerAdd[4] : r0_44, r0_46
|
||||
# 181| r0_48(int) = Load : &:r0_47, ~mu0_2
|
||||
# 181| r0_49(glval<int>) = VariableAddress[x] :
|
||||
# 181| mu0_50(int) = Store : &:r0_49, r0_48
|
||||
@@ -826,7 +826,7 @@ ir.cpp:
|
||||
# 182| r0_52(int *) = Convert : r0_51
|
||||
# 182| r0_53(glval<int>) = VariableAddress[i] :
|
||||
# 182| r0_54(int) = Load : &:r0_53, ~mu0_2
|
||||
# 182| r0_55(int *) = PointerAdd[4] : r0_52, r0_54
|
||||
# 182| r0_55(glval<int>) = PointerAdd[4] : r0_52, r0_54
|
||||
# 182| r0_56(int) = Load : &:r0_55, ~mu0_2
|
||||
# 182| r0_57(glval<int>) = VariableAddress[x] :
|
||||
# 182| mu0_58(int) = Store : &:r0_57, r0_56
|
||||
@@ -836,7 +836,7 @@ ir.cpp:
|
||||
# 183| r0_62(int *) = Convert : r0_61
|
||||
# 183| r0_63(glval<int>) = VariableAddress[i] :
|
||||
# 183| r0_64(int) = Load : &:r0_63, ~mu0_2
|
||||
# 183| r0_65(int *) = PointerAdd[4] : r0_62, r0_64
|
||||
# 183| r0_65(glval<int>) = PointerAdd[4] : r0_62, r0_64
|
||||
# 183| mu0_66(int) = Store : &:r0_65, r0_60
|
||||
# 184| r0_67(glval<int>) = VariableAddress[x] :
|
||||
# 184| r0_68(int) = Load : &:r0_67, ~mu0_2
|
||||
@@ -844,7 +844,7 @@ ir.cpp:
|
||||
# 184| r0_70(int *) = Convert : r0_69
|
||||
# 184| r0_71(glval<int>) = VariableAddress[i] :
|
||||
# 184| r0_72(int) = Load : &:r0_71, ~mu0_2
|
||||
# 184| r0_73(int *) = PointerAdd[4] : r0_70, r0_72
|
||||
# 184| r0_73(glval<int>) = PointerAdd[4] : r0_70, r0_72
|
||||
# 184| mu0_74(int) = Store : &:r0_73, r0_68
|
||||
# 185| v0_75(void) = NoOp :
|
||||
# 171| v0_76(void) = ReturnVoid :
|
||||
@@ -863,7 +863,7 @@ ir.cpp:
|
||||
# 188| r0_7(char *) = Convert : r0_6
|
||||
# 188| r0_8(glval<int>) = VariableAddress[i] :
|
||||
# 188| r0_9(int) = Load : &:r0_8, ~mu0_2
|
||||
# 188| r0_10(char *) = PointerAdd[1] : r0_7, r0_9
|
||||
# 188| r0_10(glval<char>) = PointerAdd[1] : r0_7, r0_9
|
||||
# 188| r0_11(char) = Load : &:r0_10, ~mu0_2
|
||||
# 188| mu0_12(char) = Store : &:r0_5, r0_11
|
||||
# 189| r0_13(glval<wchar_t *>) = VariableAddress[pwc] :
|
||||
@@ -876,7 +876,7 @@ ir.cpp:
|
||||
# 190| r0_20(wchar_t *) = Load : &:r0_19, ~mu0_2
|
||||
# 190| r0_21(glval<int>) = VariableAddress[i] :
|
||||
# 190| r0_22(int) = Load : &:r0_21, ~mu0_2
|
||||
# 190| r0_23(wchar_t *) = PointerAdd[4] : r0_20, r0_22
|
||||
# 190| r0_23(glval<wchar_t>) = PointerAdd[4] : r0_20, r0_22
|
||||
# 190| r0_24(wchar_t) = Load : &:r0_23, ~mu0_2
|
||||
# 190| mu0_25(wchar_t) = Store : &:r0_18, r0_24
|
||||
# 191| v0_26(void) = NoOp :
|
||||
@@ -2402,35 +2402,35 @@ ir.cpp:
|
||||
# 520| r0_7(glval<int[3]>) = VariableAddress[a1] :
|
||||
# 520| mu0_8(int[3]) = Uninitialized[a1] : &:r0_7
|
||||
# 520| r0_9(int) = Constant[0] :
|
||||
# 520| r0_10(glval<int>) = PointerAdd : r0_7, r0_9
|
||||
# 520| r0_10(glval<int>) = PointerAdd[4] : r0_7, r0_9
|
||||
# 520| r0_11(unknown[12]) = Constant[0] :
|
||||
# 520| mu0_12(unknown[12]) = Store : &:r0_10, r0_11
|
||||
# 521| r0_13(glval<int[3]>) = VariableAddress[a2] :
|
||||
# 521| mu0_14(int[3]) = Uninitialized[a2] : &:r0_13
|
||||
# 521| r0_15(int) = Constant[0] :
|
||||
# 521| r0_16(glval<int>) = PointerAdd : r0_13, r0_15
|
||||
# 521| r0_16(glval<int>) = PointerAdd[4] : r0_13, r0_15
|
||||
# 521| r0_17(glval<int>) = VariableAddress[x] :
|
||||
# 521| r0_18(int) = Load : &:r0_17, ~mu0_2
|
||||
# 521| mu0_19(int) = Store : &:r0_16, r0_18
|
||||
# 521| r0_20(int) = Constant[1] :
|
||||
# 521| r0_21(glval<int>) = PointerAdd : r0_13, r0_20
|
||||
# 521| r0_21(glval<int>) = PointerAdd[4] : r0_13, r0_20
|
||||
# 521| r0_22(glval<float>) = VariableAddress[f] :
|
||||
# 521| r0_23(float) = Load : &:r0_22, ~mu0_2
|
||||
# 521| r0_24(int) = Convert : r0_23
|
||||
# 521| mu0_25(int) = Store : &:r0_21, r0_24
|
||||
# 521| r0_26(int) = Constant[2] :
|
||||
# 521| r0_27(glval<int>) = PointerAdd : r0_13, r0_26
|
||||
# 521| r0_27(glval<int>) = PointerAdd[4] : r0_13, r0_26
|
||||
# 521| r0_28(int) = Constant[0] :
|
||||
# 521| mu0_29(int) = Store : &:r0_27, r0_28
|
||||
# 522| r0_30(glval<int[3]>) = VariableAddress[a3] :
|
||||
# 522| mu0_31(int[3]) = Uninitialized[a3] : &:r0_30
|
||||
# 522| r0_32(int) = Constant[0] :
|
||||
# 522| r0_33(glval<int>) = PointerAdd : r0_30, r0_32
|
||||
# 522| r0_33(glval<int>) = PointerAdd[4] : r0_30, r0_32
|
||||
# 522| r0_34(glval<int>) = VariableAddress[x] :
|
||||
# 522| r0_35(int) = Load : &:r0_34, ~mu0_2
|
||||
# 522| mu0_36(int) = Store : &:r0_33, r0_35
|
||||
# 522| r0_37(int) = Constant[1] :
|
||||
# 522| r0_38(glval<int>) = PointerAdd : r0_30, r0_37
|
||||
# 522| r0_38(glval<int>) = PointerAdd[4] : r0_30, r0_37
|
||||
# 522| r0_39(unknown[8]) = Constant[0] :
|
||||
# 522| mu0_40(unknown[8]) = Store : &:r0_38, r0_39
|
||||
# 523| v0_41(void) = NoOp :
|
||||
@@ -2607,7 +2607,7 @@ ir.cpp:
|
||||
# 572| mu0_6(char[1]) = Store : &:r0_3, r0_5
|
||||
# 572| r0_7(unknown[31]) = Constant[0] :
|
||||
# 572| r0_8(int) = Constant[1] :
|
||||
# 572| r0_9(glval<char>) = PointerAdd : r0_3, r0_8
|
||||
# 572| r0_9(glval<char>) = PointerAdd[1] : r0_3, r0_8
|
||||
# 572| mu0_10(unknown[31]) = Store : &:r0_9, r0_7
|
||||
# 573| r0_11(glval<char[4]>) = VariableAddress[a_nopad] :
|
||||
# 573| r0_12(glval<char[4]>) = StringConstant["foo"] :
|
||||
@@ -2622,37 +2622,37 @@ ir.cpp:
|
||||
# 576| r0_21(glval<char[2]>) = VariableAddress[c] :
|
||||
# 576| mu0_22(char[2]) = Uninitialized[c] : &:r0_21
|
||||
# 576| r0_23(int) = Constant[0] :
|
||||
# 576| r0_24(glval<char>) = PointerAdd : r0_21, r0_23
|
||||
# 576| r0_24(glval<char>) = PointerAdd[1] : r0_21, r0_23
|
||||
# 576| r0_25(unknown[2]) = Constant[0] :
|
||||
# 576| mu0_26(unknown[2]) = Store : &:r0_24, r0_25
|
||||
# 577| r0_27(glval<char[2]>) = VariableAddress[d] :
|
||||
# 577| mu0_28(char[2]) = Uninitialized[d] : &:r0_27
|
||||
# 577| r0_29(int) = Constant[0] :
|
||||
# 577| r0_30(glval<char>) = PointerAdd : r0_27, r0_29
|
||||
# 577| r0_30(glval<char>) = PointerAdd[1] : r0_27, r0_29
|
||||
# 577| r0_31(char) = Constant[0] :
|
||||
# 577| mu0_32(char) = Store : &:r0_30, r0_31
|
||||
# 577| r0_33(int) = Constant[1] :
|
||||
# 577| r0_34(glval<char>) = PointerAdd : r0_27, r0_33
|
||||
# 577| r0_34(glval<char>) = PointerAdd[1] : r0_27, r0_33
|
||||
# 577| r0_35(char) = Constant[0] :
|
||||
# 577| mu0_36(char) = Store : &:r0_34, r0_35
|
||||
# 578| r0_37(glval<char[2]>) = VariableAddress[e] :
|
||||
# 578| mu0_38(char[2]) = Uninitialized[e] : &:r0_37
|
||||
# 578| r0_39(int) = Constant[0] :
|
||||
# 578| r0_40(glval<char>) = PointerAdd : r0_37, r0_39
|
||||
# 578| r0_40(glval<char>) = PointerAdd[1] : r0_37, r0_39
|
||||
# 578| r0_41(char) = Constant[0] :
|
||||
# 578| mu0_42(char) = Store : &:r0_40, r0_41
|
||||
# 578| r0_43(int) = Constant[1] :
|
||||
# 578| r0_44(glval<char>) = PointerAdd : r0_37, r0_43
|
||||
# 578| r0_44(glval<char>) = PointerAdd[1] : r0_37, r0_43
|
||||
# 578| r0_45(char) = Constant[1] :
|
||||
# 578| mu0_46(char) = Store : &:r0_44, r0_45
|
||||
# 579| r0_47(glval<char[3]>) = VariableAddress[f] :
|
||||
# 579| mu0_48(char[3]) = Uninitialized[f] : &:r0_47
|
||||
# 579| r0_49(int) = Constant[0] :
|
||||
# 579| r0_50(glval<char>) = PointerAdd : r0_47, r0_49
|
||||
# 579| r0_50(glval<char>) = PointerAdd[1] : r0_47, r0_49
|
||||
# 579| r0_51(char) = Constant[0] :
|
||||
# 579| mu0_52(char) = Store : &:r0_50, r0_51
|
||||
# 579| r0_53(int) = Constant[1] :
|
||||
# 579| r0_54(glval<char>) = PointerAdd : r0_47, r0_53
|
||||
# 579| r0_54(glval<char>) = PointerAdd[1] : r0_47, r0_53
|
||||
# 579| r0_55(unknown[2]) = Constant[0] :
|
||||
# 579| mu0_56(unknown[2]) = Store : &:r0_54, r0_55
|
||||
# 580| v0_57(void) = NoOp :
|
||||
@@ -2980,7 +2980,7 @@ ir.cpp:
|
||||
# 694| r0_10(int(&)[10]) = Load : &:r0_9, ~mu0_2
|
||||
# 694| r0_11(int *) = Convert : r0_10
|
||||
# 694| r0_12(int) = Constant[5] :
|
||||
# 694| r0_13(int *) = PointerAdd[4] : r0_11, r0_12
|
||||
# 694| r0_13(glval<int>) = PointerAdd[4] : r0_11, r0_12
|
||||
# 694| r0_14(int) = Load : &:r0_13, ~mu0_2
|
||||
# 694| mu0_15(int) = Store : &:r0_8, r0_14
|
||||
# 695| v0_16(void) = NoOp :
|
||||
@@ -3898,14 +3898,14 @@ ir.cpp:
|
||||
# 875| r0_14(glval<char[5]>) = VariableAddress[a] :
|
||||
# 875| r0_15(char *) = Convert : r0_14
|
||||
# 875| r0_16(int) = Constant[0] :
|
||||
# 875| r0_17(char *) = PointerAdd[1] : r0_15, r0_16
|
||||
# 875| r0_17(glval<char>) = PointerAdd[1] : r0_15, r0_16
|
||||
# 875| r0_18(char *) = Convert : r0_17
|
||||
# 875| r0_19(glval<char *>) = VariableAddress[p] :
|
||||
# 875| mu0_20(char *) = Store : &:r0_19, r0_18
|
||||
# 876| r0_21(glval<char[5]>) = StringConstant["test"] :
|
||||
# 876| r0_22(char *) = Convert : r0_21
|
||||
# 876| r0_23(int) = Constant[0] :
|
||||
# 876| r0_24(char *) = PointerAdd[1] : r0_22, r0_23
|
||||
# 876| r0_24(glval<char>) = PointerAdd[1] : r0_22, r0_23
|
||||
# 876| r0_25(glval<char *>) = VariableAddress[p] :
|
||||
# 876| mu0_26(char *) = Store : &:r0_25, r0_24
|
||||
# 877| r0_27(glval<char(&)[5]>) = VariableAddress[ra] :
|
||||
@@ -4197,30 +4197,30 @@ ir.cpp:
|
||||
# 962| r0_3(glval<int[1000]>) = VariableAddress[a1] :
|
||||
# 962| mu0_4(int[1000]) = Uninitialized[a1] : &:r0_3
|
||||
# 962| r0_5(int) = Constant[0] :
|
||||
# 962| r0_6(glval<int>) = PointerAdd : r0_3, r0_5
|
||||
# 962| r0_6(glval<int>) = PointerAdd[4] : r0_3, r0_5
|
||||
# 962| r0_7(unknown[8]) = Constant[0] :
|
||||
# 962| mu0_8(unknown[8]) = Store : &:r0_6, r0_7
|
||||
# 962| r0_9(int) = Constant[2] :
|
||||
# 962| r0_10(glval<int>) = PointerAdd : r0_3, r0_9
|
||||
# 962| r0_10(glval<int>) = PointerAdd[4] : r0_3, r0_9
|
||||
# 962| r0_11(int) = Constant[10002] :
|
||||
# 962| mu0_12(int) = Store : &:r0_10, r0_11
|
||||
# 962| r0_13(int) = Constant[3] :
|
||||
# 962| r0_14(glval<int>) = PointerAdd : r0_3, r0_13
|
||||
# 962| r0_14(glval<int>) = PointerAdd[4] : r0_3, r0_13
|
||||
# 962| r0_15(unknown[3588]) = Constant[0] :
|
||||
# 962| mu0_16(unknown[3588]) = Store : &:r0_14, r0_15
|
||||
# 962| r0_17(int) = Constant[900] :
|
||||
# 962| r0_18(glval<int>) = PointerAdd : r0_3, r0_17
|
||||
# 962| r0_18(glval<int>) = PointerAdd[4] : r0_3, r0_17
|
||||
# 962| r0_19(int) = Constant[10900] :
|
||||
# 962| mu0_20(int) = Store : &:r0_18, r0_19
|
||||
# 962| r0_21(int) = Constant[901] :
|
||||
# 962| r0_22(glval<int>) = PointerAdd : r0_3, r0_21
|
||||
# 962| r0_22(glval<int>) = PointerAdd[4] : r0_3, r0_21
|
||||
# 962| r0_23(unknown[396]) = Constant[0] :
|
||||
# 962| mu0_24(unknown[396]) = Store : &:r0_22, r0_23
|
||||
# 963| r0_25(glval<int>) = VariableAddress[#return] :
|
||||
# 963| r0_26(glval<int[1000]>) = VariableAddress[a1] :
|
||||
# 963| r0_27(int *) = Convert : r0_26
|
||||
# 963| r0_28(int) = Constant[900] :
|
||||
# 963| r0_29(int *) = PointerAdd[4] : r0_27, r0_28
|
||||
# 963| r0_29(glval<int>) = PointerAdd[4] : r0_27, r0_28
|
||||
# 963| r0_30(int) = Load : &:r0_29, ~mu0_2
|
||||
# 963| mu0_31(int) = Store : &:r0_25, r0_30
|
||||
# 961| r0_32(glval<int>) = VariableAddress[#return] :
|
||||
@@ -4387,7 +4387,7 @@ ir.cpp:
|
||||
# 988| r0_8(glval<int *>) = VariableAddress[a] :
|
||||
# 988| r0_9(int *) = Load : &:r0_8, ~mu0_2
|
||||
# 988| r0_10(int) = Constant[0] :
|
||||
# 988| r0_11(int *) = PointerAdd[4] : r0_9, r0_10
|
||||
# 988| r0_11(glval<int>) = PointerAdd[4] : r0_9, r0_10
|
||||
# 988| r0_12(int) = Load : &:r0_11, ~mu0_2
|
||||
# 988| r0_13(glval<..(*)(..)>) = VariableAddress[fn] :
|
||||
# 988| r0_14(..(*)(..)) = Load : &:r0_13, ~mu0_2
|
||||
@@ -4748,7 +4748,7 @@ ir.cpp:
|
||||
#-----| r0_14(glval<int &>) = FieldAddress[x] : r0_13
|
||||
#-----| r0_15(int &) = Load : &:r0_14, ~mu0_2
|
||||
# 1034| r0_16(int) = Load : &:r0_15, ~mu0_2
|
||||
# 1034| r0_17(char *) = PointerAdd[1] : r0_11, r0_16
|
||||
# 1034| r0_17(glval<char>) = PointerAdd[1] : r0_11, r0_16
|
||||
# 1034| r0_18(char) = Load : &:r0_17, ~mu0_2
|
||||
# 1034| mu0_19(char) = Store : &:r0_6, r0_18
|
||||
# 1034| r0_20(glval<char>) = VariableAddress[#return] :
|
||||
@@ -4788,7 +4788,7 @@ ir.cpp:
|
||||
#-----| r0_12(lambda [] type at line 1036, col. 21 *) = CopyValue : r0_3
|
||||
#-----| r0_13(glval<int>) = FieldAddress[x] : r0_12
|
||||
#-----| r0_14(int) = Load : &:r0_13, ~mu0_2
|
||||
# 1036| r0_15(char *) = PointerAdd[1] : r0_10, r0_14
|
||||
# 1036| r0_15(glval<char>) = PointerAdd[1] : r0_10, r0_14
|
||||
# 1036| r0_16(char) = Load : &:r0_15, ~mu0_2
|
||||
# 1036| mu0_17(char) = Store : &:r0_6, r0_16
|
||||
# 1036| r0_18(glval<char>) = VariableAddress[#return] :
|
||||
@@ -4812,7 +4812,7 @@ ir.cpp:
|
||||
# 1038| r0_11(char *) = Call : func:r0_10, this:r0_9
|
||||
# 1038| mu0_12(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1038| r0_13(int) = Constant[0] :
|
||||
# 1038| r0_14(char *) = PointerAdd[1] : r0_11, r0_13
|
||||
# 1038| r0_14(glval<char>) = PointerAdd[1] : r0_11, r0_13
|
||||
# 1038| r0_15(char) = Load : &:r0_14, ~mu0_2
|
||||
# 1038| mu0_16(char) = Store : &:r0_6, r0_15
|
||||
# 1038| r0_17(glval<char>) = VariableAddress[#return] :
|
||||
@@ -4867,7 +4867,7 @@ ir.cpp:
|
||||
# 1040| r0_10(char *) = Call : func:r0_9, this:r0_8
|
||||
# 1040| mu0_11(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1040| r0_12(int) = Constant[0] :
|
||||
# 1040| r0_13(char *) = PointerAdd[1] : r0_10, r0_12
|
||||
# 1040| r0_13(glval<char>) = PointerAdd[1] : r0_10, r0_12
|
||||
# 1040| r0_14(char) = Load : &:r0_13, ~mu0_2
|
||||
# 1040| mu0_15(char) = Store : &:r0_6, r0_14
|
||||
# 1040| r0_16(glval<char>) = VariableAddress[#return] :
|
||||
@@ -4893,7 +4893,7 @@ ir.cpp:
|
||||
#-----| r0_13(lambda [] type at line 1042, col. 32 *) = CopyValue : r0_3
|
||||
#-----| r0_14(glval<int>) = FieldAddress[x] : r0_13
|
||||
#-----| r0_15(int) = Load : &:r0_14, ~mu0_2
|
||||
# 1042| r0_16(char *) = PointerAdd[1] : r0_11, r0_15
|
||||
# 1042| r0_16(glval<char>) = PointerAdd[1] : r0_11, r0_15
|
||||
# 1042| r0_17(char) = Load : &:r0_16, ~mu0_2
|
||||
# 1042| mu0_18(char) = Store : &:r0_6, r0_17
|
||||
# 1042| r0_19(glval<char>) = VariableAddress[#return] :
|
||||
@@ -4928,7 +4928,7 @@ ir.cpp:
|
||||
# 1045| r0_22(int &) = Load : &:r0_21, ~mu0_2
|
||||
# 1045| r0_23(int) = Load : &:r0_22, ~mu0_2
|
||||
# 1045| r0_24(int) = Sub : r0_19, r0_23
|
||||
# 1045| r0_25(char *) = PointerAdd[1] : r0_11, r0_24
|
||||
# 1045| r0_25(glval<char>) = PointerAdd[1] : r0_11, r0_24
|
||||
# 1045| r0_26(char) = Load : &:r0_25, ~mu0_2
|
||||
# 1045| mu0_27(char) = Store : &:r0_6, r0_26
|
||||
# 1045| r0_28(glval<char>) = VariableAddress[#return] :
|
||||
@@ -5270,6 +5270,68 @@ ir.cpp:
|
||||
# 1133| v13_1(void) = ReturnVoid :
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1153| void VectorTypes(int)
|
||||
# 1153| Block 0
|
||||
# 1153| v0_0(void) = EnterFunction :
|
||||
# 1153| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1153| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1153| r0_3(glval<int>) = VariableAddress[i] :
|
||||
# 1153| mu0_4(int) = InitializeParameter[i] : &:r0_3
|
||||
# 1154| r0_5(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1154| mu0_6(__attribute((vector_size(16))) int) = Uninitialized[vi4] : &:r0_5
|
||||
# 1154| r0_7(int) = Constant[0] :
|
||||
# 1154| r0_8(glval<int>) = PointerAdd[4] : r0_5, r0_7
|
||||
# 1154| r0_9(int) = Constant[0] :
|
||||
# 1154| mu0_10(int) = Store : &:r0_8, r0_9
|
||||
# 1154| r0_11(int) = Constant[1] :
|
||||
# 1154| r0_12(glval<int>) = PointerAdd[4] : r0_5, r0_11
|
||||
# 1154| r0_13(int) = Constant[1] :
|
||||
# 1154| mu0_14(int) = Store : &:r0_12, r0_13
|
||||
# 1154| r0_15(int) = Constant[2] :
|
||||
# 1154| r0_16(glval<int>) = PointerAdd[4] : r0_5, r0_15
|
||||
# 1154| r0_17(int) = Constant[2] :
|
||||
# 1154| mu0_18(int) = Store : &:r0_16, r0_17
|
||||
# 1154| r0_19(int) = Constant[3] :
|
||||
# 1154| r0_20(glval<int>) = PointerAdd[4] : r0_5, r0_19
|
||||
# 1154| r0_21(int) = Constant[3] :
|
||||
# 1154| mu0_22(int) = Store : &:r0_20, r0_21
|
||||
# 1155| r0_23(glval<int>) = VariableAddress[x] :
|
||||
# 1155| r0_24(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1155| r0_25(glval<int>) = VariableAddress[i] :
|
||||
# 1155| r0_26(int) = Load : &:r0_25, ~mu0_2
|
||||
# 1155| r0_27(glval<int>) = PointerAdd[4] : r0_24, r0_26
|
||||
# 1155| r0_28(int) = Load : &:r0_27, ~mu0_2
|
||||
# 1155| mu0_29(int) = Store : &:r0_23, r0_28
|
||||
# 1156| r0_30(glval<int>) = VariableAddress[x] :
|
||||
# 1156| r0_31(int) = Load : &:r0_30, ~mu0_2
|
||||
# 1156| r0_32(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1156| r0_33(glval<int>) = VariableAddress[i] :
|
||||
# 1156| r0_34(int) = Load : &:r0_33, ~mu0_2
|
||||
# 1156| r0_35(glval<int>) = PointerAdd[4] : r0_32, r0_34
|
||||
# 1156| mu0_36(int) = Store : &:r0_35, r0_31
|
||||
# 1157| r0_37(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4_shuffle] :
|
||||
# 1157| r0_38(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1157| r0_39(__attribute((vector_size(16))) int) = Load : &:r0_38, ~mu0_2
|
||||
# 1157| r0_40(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1157| r0_41(__attribute((vector_size(16))) int) = Load : &:r0_40, ~mu0_2
|
||||
#-----| r0_42(int) = Constant[3] :
|
||||
# 1157| r0_43(int) = Constant[2] :
|
||||
# 1157| r0_44(int) = Constant[1] :
|
||||
# 1157| r0_45(int) = Constant[0] :
|
||||
# 1157| r0_46(__attribute((vector_size(16))) int) = BuiltIn[__builtin_shufflevector] : 0:r0_39, 1:r0_41, 2:r0_42, 3:r0_43, 4:r0_44, 5:r0_45
|
||||
# 1157| mu0_47(__attribute((vector_size(16))) int) = Store : &:r0_37, r0_46
|
||||
# 1158| r0_48(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1158| r0_49(__attribute((vector_size(16))) int) = Load : &:r0_48, ~mu0_2
|
||||
# 1158| r0_50(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4_shuffle] :
|
||||
# 1158| r0_51(__attribute((vector_size(16))) int) = Load : &:r0_50, ~mu0_2
|
||||
# 1158| r0_52(__attribute((vector_size(16))) int) = Add : r0_49, r0_51
|
||||
# 1158| r0_53(glval<__attribute((vector_size(16))) int>) = VariableAddress[vi4] :
|
||||
# 1158| mu0_54(__attribute((vector_size(16))) int) = Store : &:r0_53, r0_52
|
||||
# 1159| v0_55(void) = NoOp :
|
||||
# 1153| v0_56(void) = ReturnVoid :
|
||||
# 1153| v0_57(void) = UnmodeledUse : mu*
|
||||
# 1153| v0_58(void) = ExitFunction :
|
||||
|
||||
perf-regression.cpp:
|
||||
# 6| void Big::Big()
|
||||
# 6| Block 0
|
||||
@@ -5279,7 +5341,7 @@ perf-regression.cpp:
|
||||
# 6| r0_3(glval<Big>) = InitializeThis :
|
||||
# 6| r0_4(glval<char[1073741824]>) = FieldAddress[buffer] : r0_3
|
||||
# 6| r0_5(int) = Constant[0] :
|
||||
# 6| r0_6(glval<char>) = PointerAdd : r0_4, r0_5
|
||||
# 6| r0_6(glval<char>) = PointerAdd[1] : r0_4, r0_5
|
||||
# 6| r0_7(unknown[1073741824]) = Constant[0] :
|
||||
# 6| mu0_8(unknown[1073741824]) = Store : &:r0_6, r0_7
|
||||
# 6| v0_9(void) = NoOp :
|
||||
|
||||
@@ -9,7 +9,6 @@ missingOperandType
|
||||
instructionWithoutSuccessor
|
||||
| VacuousDestructorCall.cpp:2:29:2:29 | InitializeParameter: y |
|
||||
| assume0.cpp:7:2:7:2 | Chi: call to f |
|
||||
| builtin.c:15:18:15:21 | VariableAddress: definition of vec2 |
|
||||
| condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt |
|
||||
| condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt |
|
||||
| condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt |
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
missingOperand
|
||||
| builtin.c:15:25:15:71 | Store: __builtin_shufflevector | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:18:3:18:80 | Convert: (void)... | Instruction 'Convert' is missing an expected operand with tag 'Unary' in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| condition_decls.cpp:16:6:16:20 | ConditionalBranch: (condition decl) | Instruction 'ConditionalBranch' is missing an expected operand with tag 'Condition' in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) |
|
||||
| condition_decls.cpp:26:3:36:3 | Switch: switch (...) ... | Instruction 'Switch' is missing an expected operand with tag 'Condition' in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) |
|
||||
| condition_decls.cpp:41:9:41:23 | ConditionalBranch: (condition decl) | Instruction 'ConditionalBranch' is missing an expected operand with tag 'Condition' in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) |
|
||||
@@ -27,18 +25,6 @@ instructionWithoutSuccessor
|
||||
| VacuousDestructorCall.cpp:4:3:4:3 | Load: y |
|
||||
| assume0.cpp:7:2:7:2 | CallSideEffect: call to f |
|
||||
| assume0.cpp:9:11:9:11 | Constant: (bool)... |
|
||||
| builtin.c:14:26:14:26 | Constant: 0 |
|
||||
| builtin.c:14:29:14:29 | Constant: 1 |
|
||||
| builtin.c:14:32:14:32 | Constant: 2 |
|
||||
| builtin.c:14:35:14:35 | Constant: 3 |
|
||||
| builtin.c:15:18:15:21 | VariableAddress: definition of vec2 |
|
||||
| builtin.c:15:25:15:71 | Store: __builtin_shufflevector |
|
||||
| builtin.c:15:49:15:51 | Load: vec |
|
||||
| builtin.c:15:54:15:56 | Load: vec |
|
||||
| builtin.c:15:64:15:64 | Constant: 2 |
|
||||
| builtin.c:15:67:15:67 | Constant: 1 |
|
||||
| builtin.c:15:70:15:70 | Constant: 0 |
|
||||
| builtin.c:18:33:18:35 | Load: vec |
|
||||
| condition_decls.cpp:16:19:16:20 | CallSideEffect: call to BoxedInt |
|
||||
| condition_decls.cpp:26:19:26:19 | CallSideEffect: call to operator int |
|
||||
| condition_decls.cpp:26:23:26:24 | CallSideEffect: call to BoxedInt |
|
||||
@@ -48,7 +34,6 @@ instructionWithoutSuccessor
|
||||
| file://:0:0:0:0 | CompareNE: (bool)... |
|
||||
| file://:0:0:0:0 | CompareNE: (bool)... |
|
||||
| file://:0:0:0:0 | CompareNE: (bool)... |
|
||||
| file://:0:0:0:0 | Constant: ... + ... |
|
||||
| misc.c:171:10:171:13 | Uninitialized: definition of str2 |
|
||||
| misc.c:171:15:171:31 | Add: ... + ... |
|
||||
| misc.c:173:14:173:26 | Mul: ... * ... |
|
||||
@@ -625,48 +610,6 @@ backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
| VacuousDestructorCall.cpp:4:3:4:3 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | IR: CallDestructor | void CallDestructor<int>(int, int*) |
|
||||
| assume0.cpp:11:2:11:2 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | assume0.cpp:5:6:5:6 | IR: h | void h() |
|
||||
| builtin.c:5:5:5:11 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:15:25:15:71 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:15:49:15:51 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:15:54:15:56 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:18:33:18:35 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:20:3:20:36 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:20:10:20:31 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:20:33:20:33 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:21:3:21:39 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:21:10:21:31 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:21:33:21:33 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:21:38:21:38 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:22:3:22:40 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:22:10:22:32 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:22:34:22:34 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:22:39:22:39 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:24:7:24:7 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:25:5:25:25 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:28:7:28:29 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:28:31:28:33 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:29:12:29:14 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:34:3:34:35 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:34:10:34:20 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:34:34:34:34 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:39:3:39:46 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:39:10:39:23 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:39:25:39:25 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:39:30:39:30 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:43:3:43:24 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:43:26:43:26 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:43:37:43:37 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:45:3:45:42 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:45:10:45:31 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:47:7:47:22 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:48:2:48:6 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:51:3:51:47 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:51:10:51:27 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:51:41:51:41 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:51:43:51:43 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:54:3:54:42 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:54:10:54:26 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| builtin.c:56:10:56:12 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | builtin.c:5:5:5:11 | IR: builtin | int builtin(int, int) |
|
||||
| condition_decls.cpp:16:15:16:15 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) |
|
||||
| condition_decls.cpp:16:15:16:16 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) |
|
||||
| condition_decls.cpp:17:5:17:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) |
|
||||
|
||||
@@ -18,7 +18,6 @@ missingOperandType
|
||||
instructionWithoutSuccessor
|
||||
| VacuousDestructorCall.cpp:2:29:2:29 | InitializeParameter: y |
|
||||
| assume0.cpp:7:2:7:2 | CallSideEffect: call to f |
|
||||
| builtin.c:15:18:15:21 | VariableAddress: definition of vec2 |
|
||||
| condition_decls.cpp:16:19:16:20 | CallSideEffect: call to BoxedInt |
|
||||
| condition_decls.cpp:26:23:26:24 | CallSideEffect: call to BoxedInt |
|
||||
| condition_decls.cpp:41:22:41:23 | CallSideEffect: call to BoxedInt |
|
||||
|
||||
7
cpp/ql/test/library-tests/usings/Usings2.expected
Normal file
7
cpp/ql/test/library-tests/usings/Usings2.expected
Normal file
@@ -0,0 +1,7 @@
|
||||
| templates.cpp:9:5:9:14 | using c | file://:0:0:0:0 | std |
|
||||
| usings.cpp:8:1:8:11 | using nf | file://:0:0:0:0 | (global namespace) |
|
||||
| usings.cpp:9:1:9:17 | using namespace N | file://:0:0:0:0 | (global namespace) |
|
||||
| usings.cpp:18:3:18:13 | using bf | usings.cpp:16:8:16:8 | D |
|
||||
| usings.cpp:21:5:21:14 | using gf | usings.cpp:20:13:23:3 | { ... } |
|
||||
| usings.cpp:34:3:34:20 | using tbf | usings.cpp:32:8:32:9 | TD |
|
||||
| usings.cpp:42:5:42:22 | using foo | usings.cpp:41:11:41:15 | nsbar |
|
||||
6
cpp/ql/test/library-tests/usings/Usings2.ql
Normal file
6
cpp/ql/test/library-tests/usings/Usings2.ql
Normal file
@@ -0,0 +1,6 @@
|
||||
import cpp
|
||||
|
||||
from UsingEntry ue, Element e
|
||||
where
|
||||
e = ue.getEnclosingElement()
|
||||
select ue, e
|
||||
@@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.frameworks.System
|
||||
import semmle.code.csharp.frameworks.system.Net
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
@@ -20,13 +19,15 @@ import semmle.code.csharp.security.dataflow.SqlInjection
|
||||
import semmle.code.csharp.security.dataflow.XSS
|
||||
import semmle.code.csharp.security.dataflow.UrlRedirect
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2
|
||||
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2::PathGraph
|
||||
import semmle.code.csharp.dataflow.TaintTracking2
|
||||
|
||||
/**
|
||||
* A configuration for specifying expressions that must be
|
||||
* encoded, along with a set of potential valid encoded values.
|
||||
*/
|
||||
abstract class RequiresEncodingConfiguration extends TaintTracking::Configuration {
|
||||
abstract class RequiresEncodingConfiguration extends TaintTracking2::Configuration {
|
||||
bindingset[this]
|
||||
RequiresEncodingConfiguration() { any() }
|
||||
|
||||
@@ -61,6 +62,8 @@ abstract class RequiresEncodingConfiguration extends TaintTracking::Configuratio
|
||||
override predicate isSink(Node sink) { this.requiresEncoding(sink) }
|
||||
|
||||
override predicate isSanitizer(Node sanitizer) { this.isPossibleEncodedValue(sanitizer.asExpr()) }
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
/** An encoded value, for example a call to `HttpServerUtility.HtmlEncode`. */
|
||||
|
||||
@@ -182,7 +182,8 @@ class AnnotatedType extends TAnnotatedType {
|
||||
|
||||
/** Gets a textual representation of this annotated type. */
|
||||
string toString() {
|
||||
result = annotations.getTypePrefix() + getUnderlyingType().toStringWithTypes() + annotations.getTypeSuffix()
|
||||
result = annotations.getTypePrefix() + getUnderlyingType().toStringWithTypes() +
|
||||
annotations.getTypeSuffix()
|
||||
}
|
||||
|
||||
/** Gets the location of this annotated type. */
|
||||
|
||||
@@ -726,7 +726,7 @@ module AssignableDefinitions {
|
||||
class InitializerDefinition extends AssignmentDefinition {
|
||||
private Assignable fieldOrProp;
|
||||
|
||||
InitializerDefinition() { this.getAssignment().getParent() = fieldOrProp}
|
||||
InitializerDefinition() { this.getAssignment().getParent() = fieldOrProp }
|
||||
|
||||
/** Gets the assignable (field or property) being initialized. */
|
||||
Assignable getAssignable() { result = fieldOrProp }
|
||||
|
||||
@@ -26,14 +26,10 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal
|
||||
final AnnotatedType getAnnotatedReturnType() { result.appliesTo(this) }
|
||||
|
||||
/** DEPRECATED: Use `getAnnotatedReturnType().isRef()` instead. */
|
||||
deprecated predicate returnsRef() {
|
||||
this.getAnnotatedReturnType().isRef()
|
||||
}
|
||||
deprecated predicate returnsRef() { this.getAnnotatedReturnType().isRef() }
|
||||
|
||||
/** DEPRECATED: Use `getAnnotatedReturnType().isReadonlyRef()` instead. */
|
||||
deprecated predicate returnsRefReadonly() {
|
||||
this.getAnnotatedReturnType().isReadonlyRef()
|
||||
}
|
||||
deprecated predicate returnsRefReadonly() { this.getAnnotatedReturnType().isReadonlyRef() }
|
||||
|
||||
override Callable getSourceDeclaration() { result = Parameterizable.super.getSourceDeclaration() }
|
||||
|
||||
|
||||
@@ -203,6 +203,18 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
*/
|
||||
Expr getInitializer() { result = this.getChildExpr(1).getChildExpr(0) }
|
||||
|
||||
/**
|
||||
* Holds if this property has an initial value. For example, the initial
|
||||
* value of `P` on line 2 is `20` in
|
||||
*
|
||||
* ```
|
||||
* class C {
|
||||
* public int P { get; set; } = 20;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate hasInitializer() { exists(this.getInitializer()) }
|
||||
|
||||
/**
|
||||
* Gets the expression body of this property, if any. For example, the expression
|
||||
* body of `P` on line 2 is `20` in
|
||||
|
||||
@@ -364,9 +364,30 @@ class LocalConstant extends LocalVariable, @local_constant {
|
||||
*/
|
||||
class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent, DotNet::Field,
|
||||
@field {
|
||||
/** Gets the initializer of this field, if any. */
|
||||
/**
|
||||
* Gets the initial value of this field, if any. For example, the initial
|
||||
* value of `F` on line 2 is `20` in
|
||||
*
|
||||
* ```
|
||||
* class C {
|
||||
* public int F = 20;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override Expr getInitializer() { result = this.getChildExpr(0).getChildExpr(0) }
|
||||
|
||||
/**
|
||||
* Holds if this field has an initial value. For example, the initial
|
||||
* value of `F` on line 2 is `20` in
|
||||
*
|
||||
* ```
|
||||
* class C {
|
||||
* public int F = 20;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate hasInitializer() { exists(this.getInitializer()) }
|
||||
|
||||
/** Holds if this field is `volatile`. */
|
||||
predicate isVolatile() { this.hasModifier("volatile") }
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class BasicBlock extends TBasicBlockStart {
|
||||
ControlFlow::Node getLastNode() { result = getNode(length() - 1) }
|
||||
|
||||
/** Gets the callable that this basic block belongs to. */
|
||||
Callable getCallable() { result = this.getAPredecessor().getCallable() }
|
||||
final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() }
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length() { result = strictcount(getANode()) }
|
||||
@@ -346,8 +346,6 @@ private import Internal
|
||||
*/
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { entryBB(this) }
|
||||
|
||||
override Callable getCallable() { result.getEntryPoint() = this.getFirstNode() }
|
||||
}
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
|
||||
@@ -287,7 +287,11 @@ module ControlFlow {
|
||||
|
||||
ElementNode() { this = TElementNode(cfe, splits) }
|
||||
|
||||
override Callable getEnclosingCallable() { result = cfe.getEnclosingCallable() }
|
||||
override Callable getEnclosingCallable() {
|
||||
result = cfe.getEnclosingCallable()
|
||||
or
|
||||
result = this.getASplit().(InitializerSplitting::InitializerSplitImpl).getConstructor()
|
||||
}
|
||||
|
||||
override ControlFlowElement getElement() { result = cfe }
|
||||
|
||||
@@ -1750,11 +1754,38 @@ module ControlFlow {
|
||||
cfe = last(result.(JumpStmt).getChild(0), c) and
|
||||
c instanceof NormalCompletion
|
||||
or
|
||||
// Flow from constructor initializer to first element of constructor body
|
||||
cfe = any(ConstructorInitializer ci |
|
||||
c instanceof SimpleCompletion and
|
||||
result = first(ci.getConstructor().getBody())
|
||||
exists(ConstructorInitializer ci, Constructor con |
|
||||
cfe = last(ci, c) and
|
||||
con = ci.getConstructor() and
|
||||
c instanceof NormalCompletion
|
||||
|
|
||||
// Flow from constructor initializer to first member initializer
|
||||
exists(InitializerSplitting::InitializedInstanceMember m |
|
||||
InitializerSplitting::constructorInitializeOrder(con, m, 0)
|
||||
|
|
||||
result = first(m.getInitializer())
|
||||
)
|
||||
or
|
||||
// Flow from constructor initializer to first element of constructor body
|
||||
not InitializerSplitting::constructorInitializeOrder(con, _, _) and
|
||||
result = first(con.getBody())
|
||||
)
|
||||
or
|
||||
exists(Constructor con, InitializerSplitting::InitializedInstanceMember m, int i |
|
||||
cfe = last(m.getInitializer(), c) and
|
||||
c instanceof NormalCompletion and
|
||||
InitializerSplitting::constructorInitializeOrder(con, m, i)
|
||||
|
|
||||
// Flow from one member initializer to the next
|
||||
exists(InitializerSplitting::InitializedInstanceMember next |
|
||||
InitializerSplitting::constructorInitializeOrder(con, next, i + 1) and
|
||||
result = first(next.getInitializer())
|
||||
)
|
||||
or
|
||||
// Flow from last member initializer to constructor body
|
||||
m = InitializerSplitting::lastConstructorInitializer(con) and
|
||||
result = first(con.getBody())
|
||||
)
|
||||
or
|
||||
// Flow from element with `goto` completion to first element of relevant
|
||||
// target
|
||||
@@ -1831,11 +1862,18 @@ module ControlFlow {
|
||||
p = any(Callable c |
|
||||
if exists(c.(Constructor).getInitializer())
|
||||
then result = first(c.(Constructor).getInitializer())
|
||||
else result = first(c.getBody())
|
||||
else
|
||||
if InitializerSplitting::constructorInitializes(c, _)
|
||||
then
|
||||
result = first(any(InitializerSplitting::InitializedInstanceMember m |
|
||||
InitializerSplitting::constructorInitializeOrder(c, m, 0)
|
||||
).getInitializer())
|
||||
else result = first(c.getBody())
|
||||
)
|
||||
or
|
||||
expr_parent_top_level_adjusted(any(Expr e | result = first(e)), _, p) and
|
||||
not p instanceof Callable
|
||||
not p instanceof Callable and
|
||||
not p instanceof InitializerSplitting::InitializedInstanceMember
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1845,6 +1883,12 @@ module ControlFlow {
|
||||
Callable succExit(ControlFlowElement cfe, Completion c) {
|
||||
cfe = last(result.getBody(), c) and
|
||||
not c instanceof GotoCompletion
|
||||
or
|
||||
exists(InitializerSplitting::InitializedInstanceMember m |
|
||||
m = InitializerSplitting::lastConstructorInitializer(result) and
|
||||
cfe = last(m.getInitializer(), c) and
|
||||
not result.hasBody()
|
||||
)
|
||||
}
|
||||
}
|
||||
import Successor
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.controlflow.internal.Completion
|
||||
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks
|
||||
private import semmle.code.csharp.controlflow.internal.PreSsa as PreSsa
|
||||
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow::Internal::Successor
|
||||
private import semmle.code.csharp.controlflow.Guards as Guards
|
||||
private import ControlFlow
|
||||
private import SuccessorTypes
|
||||
|
||||
@@ -28,12 +26,14 @@ private module Cached {
|
||||
|
||||
cached
|
||||
newtype TSplitKind =
|
||||
TInitializerSplitKind() or
|
||||
TFinallySplitKind() or
|
||||
TExceptionHandlerSplitKind() or
|
||||
TBooleanSplitKind(BooleanSplitting::BooleanSplitSubKind kind) { kind.startsSplit(_) }
|
||||
|
||||
cached
|
||||
newtype TSplit =
|
||||
TInitializerSplit(Constructor c) { InitializerSplitting::constructorInitializes(c, _) } or
|
||||
TFinallySplit(FinallySplitting::FinallySplitType type) or
|
||||
TExceptionHandlerSplit(ExceptionClass ec) or
|
||||
TBooleanSplit(BooleanSplitting::BooleanSplitSubKind kind, boolean branch) {
|
||||
@@ -51,6 +51,8 @@ private module Cached {
|
||||
case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) and
|
||||
head = case2aSomeAtRank(pred, predSplits, succ, c, rnk)
|
||||
)
|
||||
or
|
||||
succEntrySplitsCons(_, _, head, tail, _)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -118,11 +120,14 @@ abstract class SplitKind extends TSplitKind {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this split kind should be included when constructing the control
|
||||
* flow graph for callable `c`. For performance reasons, the number of splits
|
||||
* is restricted by the `maxSplits()` predicate.
|
||||
* Holds if this split kind is enabled for control flow element `cfe`. For
|
||||
* performance reasons, the number of splits is restricted by the `maxSplits()`
|
||||
* predicate.
|
||||
*/
|
||||
private predicate isEnabled(Callable c) { this.getRank(c) <= maxSplits() }
|
||||
predicate isEnabled(ControlFlowElement cfe) {
|
||||
this.appliesTo(cfe) and
|
||||
this.getRank(cfe.getEnclosingCallable()) <= maxSplits()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rank of this split kind among all the split kinds that apply to
|
||||
@@ -130,8 +135,7 @@ abstract class SplitKind extends TSplitKind {
|
||||
* `getListOrder()`.
|
||||
*/
|
||||
int getListRank(ControlFlowElement cfe) {
|
||||
this.appliesTo(cfe) and
|
||||
this.isEnabled(cfe.getEnclosingCallable()) and
|
||||
this.isEnabled(cfe) and
|
||||
this = rank[result](SplitKind sk | sk.appliesTo(cfe) | sk order by sk.getListOrder())
|
||||
}
|
||||
|
||||
@@ -153,6 +157,14 @@ abstract class SplitInternal extends SplitImpl {
|
||||
*/
|
||||
abstract predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c);
|
||||
|
||||
/**
|
||||
* Holds if this split is entered when control passes from `c` to the entry point
|
||||
* `succ`.
|
||||
*
|
||||
* Invariant: `hasEntry(c, succ) implies succ = Successor::succEntry(c)`.
|
||||
*/
|
||||
abstract predicate hasEntry(Callable c, ControlFlowElement succ);
|
||||
|
||||
/**
|
||||
* Holds if this split is left when control passes from `pred` to `succ` with
|
||||
* completion `c`.
|
||||
@@ -163,11 +175,11 @@ abstract class SplitInternal extends SplitImpl {
|
||||
|
||||
/**
|
||||
* Holds if this split is left when control passes from `pred` out of the enclosing
|
||||
* callable with completion `c`.
|
||||
* callable `result` with completion `c`.
|
||||
*
|
||||
* Invariant: `hasExit(pred, c) implies pred.getEnclosingCallable() = Successor::succExit(pred, c)`
|
||||
* Invariant: `succ = hasExit(pred, c) implies succ = Successor::succExit(pred, c)`
|
||||
*/
|
||||
abstract predicate hasExit(ControlFlowElement pred, Completion c);
|
||||
abstract Callable hasExit(ControlFlowElement pred, Completion c);
|
||||
|
||||
/**
|
||||
* Holds if this split is maintained when control passes from `pred` to `succ` with
|
||||
@@ -181,10 +193,197 @@ abstract class SplitInternal extends SplitImpl {
|
||||
final predicate appliesTo(ControlFlowElement cfe) {
|
||||
this.hasEntry(_, cfe, _)
|
||||
or
|
||||
this.hasEntry(_, cfe)
|
||||
or
|
||||
exists(ControlFlowElement pred | this.appliesTo(pred) | this.hasSuccessor(pred, cfe, _))
|
||||
}
|
||||
}
|
||||
|
||||
module InitializerSplitting {
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
|
||||
/**
|
||||
* A non-static member with an initializer, for example a field `int Field = 0`.
|
||||
*/
|
||||
class InitializedInstanceMember extends Member {
|
||||
private AssignExpr ae;
|
||||
|
||||
InitializedInstanceMember() {
|
||||
not this.isStatic() and
|
||||
expr_parent_top_level_adjusted(ae, _, this)
|
||||
}
|
||||
|
||||
/** Gets the initializer expression. */
|
||||
AssignExpr getInitializer() { result = ae }
|
||||
|
||||
/**
|
||||
* Gets a control flow element that is a syntactic descendant of the
|
||||
* initializer expression.
|
||||
*/
|
||||
ControlFlowElement getAnInitializerDescendant() {
|
||||
result = ae
|
||||
or
|
||||
result = this.getAnInitializerDescendant().getAChild()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a non-static constructor that performs the initialization
|
||||
* of member `m`.
|
||||
*/
|
||||
predicate constructorInitializes(Constructor c, InitializedInstanceMember m) {
|
||||
c = c.getSourceDeclaration() and
|
||||
not c.isStatic() and
|
||||
c.getDeclaringType().hasMember(m) and
|
||||
(
|
||||
not c.hasInitializer()
|
||||
or
|
||||
// Members belonging to the base class are initialized via calls to the
|
||||
// base constructor
|
||||
c.getInitializer().isBase() and
|
||||
m.getDeclaringType() = c.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is the `i`th member initialized by non-static constructor `c`.
|
||||
*/
|
||||
predicate constructorInitializeOrder(Constructor c, InitializedInstanceMember m, int i) {
|
||||
constructorInitializes(c, m) and
|
||||
m = rank[i + 1](InitializedInstanceMember m0 |
|
||||
constructorInitializes(c, m0)
|
||||
|
|
||||
m0
|
||||
order by
|
||||
m0.getLocation().getStartLine(), m0.getLocation().getStartColumn(),
|
||||
m0.getLocation().getFile().getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the last member initialized by non-static constructor `c`. */
|
||||
InitializedInstanceMember lastConstructorInitializer(Constructor c) {
|
||||
exists(int i |
|
||||
constructorInitializeOrder(c, result, i) and
|
||||
not constructorInitializeOrder(c, _, i + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A split for non-static member initializers belonging to a given non-static
|
||||
* constructor. For example, in
|
||||
*
|
||||
* ```
|
||||
* class C
|
||||
* {
|
||||
* int Field1 = 0;
|
||||
* int Field2 = Field1 + 1;
|
||||
* int Field3;
|
||||
*
|
||||
* public C()
|
||||
* {
|
||||
* Field3 = 2;
|
||||
* }
|
||||
*
|
||||
* public C(int i)
|
||||
* {
|
||||
* Field3 = 3;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* the initializer expressions `Field1 = 0` and `Field2 = Field1 + 1` are split
|
||||
* on the two constructors. This is in order to generate CFGs for the two
|
||||
* constructors that mimic
|
||||
*
|
||||
* ```
|
||||
* public C()
|
||||
* {
|
||||
* Field1 = 0;
|
||||
* Field2 = Field1 + 1;
|
||||
* Field3 = 2;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* and
|
||||
*
|
||||
* ```
|
||||
* public C()
|
||||
* {
|
||||
* Field1 = 0;
|
||||
* Field2 = Field1 + 1;
|
||||
* Field3 = 3;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* respectively.
|
||||
*/
|
||||
class InitializerSplitImpl extends SplitImpl, TInitializerSplit {
|
||||
private Constructor c;
|
||||
|
||||
InitializerSplitImpl() { this = TInitializerSplit(c) }
|
||||
|
||||
/** Gets the constructor. */
|
||||
Constructor getConstructor() { result = c }
|
||||
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class InitializerSplitKind extends SplitKind, TInitializerSplitKind {
|
||||
override int getListOrder() { result = 0 }
|
||||
|
||||
override predicate isEnabled(ControlFlowElement cfe) { this.appliesTo(cfe) }
|
||||
|
||||
override string toString() { result = "Initializer" }
|
||||
}
|
||||
|
||||
int getNextListOrder() { result = 1 }
|
||||
|
||||
private class InitializerSplitInternal extends SplitInternal, InitializerSplitImpl {
|
||||
override InitializerSplitKind getKind() { any() }
|
||||
|
||||
override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
|
||||
exists(ConstructorInitializer ci |
|
||||
pred = last(ci, c) and
|
||||
succ = succ(pred, c) and
|
||||
succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and
|
||||
this.getConstructor() = ci.getConstructor()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasEntry(Callable c, ControlFlowElement succ) {
|
||||
succ = succEntry(c) and
|
||||
c = this.getConstructor() and
|
||||
succ = any(InitializedInstanceMember m).getAnInitializerDescendant()
|
||||
}
|
||||
|
||||
override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
|
||||
this.appliesTo(pred) and
|
||||
succ = succ(pred, c) and
|
||||
not succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and
|
||||
succ.getEnclosingCallable() = this.getConstructor()
|
||||
}
|
||||
|
||||
override Callable hasExit(ControlFlowElement pred, Completion c) {
|
||||
this.appliesTo(pred) and
|
||||
result = succExit(pred, c) and
|
||||
result = this.getConstructor()
|
||||
}
|
||||
|
||||
override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
|
||||
this.appliesTo(pred) and
|
||||
succ = succ(pred, c) and
|
||||
succ = any(InitializedInstanceMember m | constructorInitializes(this.getConstructor(), m))
|
||||
.getAnInitializerDescendant()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ControlFlowElement getAChild(ControlFlowElement cfe, Callable c) {
|
||||
result = cfe.getAChild() and
|
||||
c = result.getEnclosingCallable()
|
||||
}
|
||||
|
||||
module FinallySplitting {
|
||||
/**
|
||||
* The type of a split `finally` node.
|
||||
@@ -208,12 +407,6 @@ module FinallySplitting {
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ControlFlowElement getAChild(ControlFlowElement cfe, Callable c) {
|
||||
result = cfe.getAChild() and
|
||||
c = result.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a descendant that belongs to the `finally` block of try statement
|
||||
* `try`.
|
||||
@@ -294,11 +487,13 @@ module FinallySplitting {
|
||||
}
|
||||
|
||||
private class FinallySplitKind extends SplitKind, TFinallySplitKind {
|
||||
override int getListOrder() { result = 0 }
|
||||
override int getListOrder() { result = InitializerSplitting::getNextListOrder() }
|
||||
|
||||
override string toString() { result = "Finally" }
|
||||
}
|
||||
|
||||
int getNextListOrder() { result = InitializerSplitting::getNextListOrder() + 1 }
|
||||
|
||||
private class FinallySplitInternal extends SplitInternal, FinallySplitImpl {
|
||||
override FinallySplitKind getKind() { any() }
|
||||
|
||||
@@ -316,6 +511,8 @@ module FinallySplitting {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasEntry(Callable c, ControlFlowElement succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this split applies to control flow element `pred`, where `pred`
|
||||
* is a valid predecessor.
|
||||
@@ -366,8 +563,8 @@ module FinallySplitting {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasExit(ControlFlowElement pred, Completion c) {
|
||||
exists(succExit(pred, c)) and
|
||||
override Callable hasExit(ControlFlowElement pred, Completion c) {
|
||||
result = succExit(pred, c) and
|
||||
(
|
||||
exit(pred, c)
|
||||
or
|
||||
@@ -445,11 +642,13 @@ module ExceptionHandlerSplitting {
|
||||
}
|
||||
|
||||
private class ExceptionHandlerSplitKind extends SplitKind, TExceptionHandlerSplitKind {
|
||||
override int getListOrder() { result = 1 }
|
||||
override int getListOrder() { result = FinallySplitting::getNextListOrder() }
|
||||
|
||||
override string toString() { result = "ExceptionHandler" }
|
||||
}
|
||||
|
||||
int getNextListOrder() { result = FinallySplitting::getNextListOrder() + 1 }
|
||||
|
||||
private class ExceptionHandlerSplitInternal extends SplitInternal, ExceptionHandlerSplitImpl {
|
||||
override ExceptionHandlerSplitKind getKind() { any() }
|
||||
|
||||
@@ -461,6 +660,8 @@ module ExceptionHandlerSplitting {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasEntry(Callable c, ControlFlowElement succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this split applies to catch clause `scc`. The parameter `match`
|
||||
* indicates whether the catch clause `scc` may match the exception type of
|
||||
@@ -539,10 +740,10 @@ module ExceptionHandlerSplitting {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasExit(ControlFlowElement pred, Completion c) {
|
||||
override Callable hasExit(ControlFlowElement pred, Completion c) {
|
||||
// Exit out from last `catch` clause (no catch clauses match)
|
||||
this.hasLastExit(pred, c) and
|
||||
exists(succExit(pred, c))
|
||||
result = succExit(pred, c)
|
||||
}
|
||||
|
||||
override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
|
||||
@@ -720,6 +921,27 @@ module BooleanSplitting {
|
||||
}
|
||||
}
|
||||
|
||||
private int getListOrder(BooleanSplitSubKind kind) {
|
||||
exists(Callable c, int r | c = kind.getEnclosingCallable() |
|
||||
result = r + ExceptionHandlerSplitting::getNextListOrder() - 1 and
|
||||
kind = rank[r](BooleanSplitSubKind kind0 |
|
||||
kind0.getEnclosingCallable() = c and
|
||||
kind0.startsSplit(_)
|
||||
|
|
||||
kind0
|
||||
order by
|
||||
kind0.getLocation().getStartLine(), kind0.getLocation().getStartColumn(),
|
||||
kind0.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
int getNextListOrder() {
|
||||
result = max(int i |
|
||||
i = getListOrder(_) + 1 or i = ExceptionHandlerSplitting::getNextListOrder()
|
||||
)
|
||||
}
|
||||
|
||||
private class BooleanSplitKind extends SplitKind, TBooleanSplitKind {
|
||||
private BooleanSplitSubKind kind;
|
||||
|
||||
@@ -728,20 +950,7 @@ module BooleanSplitting {
|
||||
/** Gets the sub kind of this Boolean split kind. */
|
||||
BooleanSplitSubKind getSubKind() { result = kind }
|
||||
|
||||
override int getListOrder() {
|
||||
exists(Callable c, int r | c = kind.getEnclosingCallable() |
|
||||
result = r + 1 and // start the ordering from 2
|
||||
kind = rank[r](BooleanSplitSubKind kind0 |
|
||||
kind0.getEnclosingCallable() = c and
|
||||
kind0.startsSplit(_)
|
||||
|
|
||||
kind0
|
||||
order by
|
||||
kind0.getLocation().getStartLine(), kind0.getLocation().getStartColumn(),
|
||||
kind0.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
override int getListOrder() { result = getListOrder(kind) }
|
||||
|
||||
override string toString() { result = kind.toString() }
|
||||
}
|
||||
@@ -755,6 +964,8 @@ module BooleanSplitting {
|
||||
this.getBranch() = c.getInnerCompletion().(BooleanCompletion).getValue()
|
||||
}
|
||||
|
||||
override predicate hasEntry(Callable c, ControlFlowElement succ) { none() }
|
||||
|
||||
private ConditionBlock getACorrelatedCondition(boolean inverted) {
|
||||
this.getSubKind().correlatesConditions(_, result, inverted)
|
||||
}
|
||||
@@ -787,10 +998,10 @@ module BooleanSplitting {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasExit(ControlFlowElement pred, Completion c) {
|
||||
override Callable hasExit(ControlFlowElement pred, Completion c) {
|
||||
exists(PreBasicBlock bb | this.appliesToBlock(bb, c) |
|
||||
pred = bb.getLastElement() and
|
||||
exists(succExit(pred, c))
|
||||
result = succExit(pred, c)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -830,6 +1041,26 @@ class Splits extends TSplits {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate succEntrySplitsFromRank(
|
||||
@top_level_exprorstmt_parent pred, ControlFlowElement succ, Splits splits, int rnk
|
||||
) {
|
||||
splits = TSplitsNil() and
|
||||
succ = succEntry(pred) and
|
||||
rnk = 0
|
||||
or
|
||||
exists(SplitInternal head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) |
|
||||
splits = TSplitsCons(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate succEntrySplitsCons(
|
||||
Callable pred, ControlFlowElement succ, SplitInternal head, Splits tail, int rnk
|
||||
) {
|
||||
succEntrySplitsFromRank(pred, succ, tail, rnk - 1) and
|
||||
head.hasEntry(pred, succ) and
|
||||
rnk = head.getKind().getListRank(succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `succ` with splits `succSplits` is the first element that is executed
|
||||
* when entering callable `pred`.
|
||||
@@ -838,9 +1069,16 @@ pragma[noinline]
|
||||
predicate succEntrySplits(
|
||||
@top_level_exprorstmt_parent pred, ControlFlowElement succ, Splits succSplits, SuccessorType t
|
||||
) {
|
||||
succ = succEntry(pred) and
|
||||
t instanceof NormalSuccessor and
|
||||
succSplits = TSplitsNil() // initially no splits
|
||||
exists(int rnk |
|
||||
succ = succEntry(pred) and
|
||||
t instanceof NormalSuccessor and
|
||||
succEntrySplitsFromRank(pred, succ, succSplits, rnk)
|
||||
|
|
||||
rnk = 0 and
|
||||
not any(SplitInternal split).hasEntry(pred, succ)
|
||||
or
|
||||
rnk = max(SplitInternal split | split.hasEntry(pred, succ) | split.getKind().getListRank(succ))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -853,7 +1091,7 @@ predicate succExitSplits(ControlFlowElement pred, Splits predSplits, Callable su
|
||||
t.matchesCompletion(c) and
|
||||
succ = succExit(pred, c) and
|
||||
forall(SplitInternal predSplit | predSplit = predSplits.getASplit() |
|
||||
predSplit.hasExit(pred, c)
|
||||
succ = predSplit.hasExit(pred, c)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,23 +7,4 @@ import csharp
|
||||
|
||||
module DataFlow {
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImpl
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Five copies are available: `DataFlow` through `DataFlow5`.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,4 @@ import csharp
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImpl2
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Five copies are available: `DataFlow` through `DataFlow5`.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,4 @@ import csharp
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImpl3
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Five copies are available: `DataFlow` through `DataFlow5`.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,4 @@ import csharp
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImpl4
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Five copies are available: `DataFlow` through `DataFlow5`.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,4 @@ import csharp
|
||||
|
||||
module DataFlow5 {
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImpl5
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Five copies are available: `DataFlow` through `DataFlow5`.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,29 +1096,27 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reachableFromDelegateCreation(Expr e) {
|
||||
delegateCreation(e, _, _)
|
||||
private predicate reachesDelegateCall(Expr e) {
|
||||
delegateCall(_, e)
|
||||
or
|
||||
exists(Expr mid | reachableFromDelegateCreation(mid) | delegateFlowStep(mid, e))
|
||||
exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate delegateFlowStepReachable(Expr pred, Expr succ) {
|
||||
private predicate delegateFlowStepReaches(Expr pred, Expr succ) {
|
||||
delegateFlowStep(pred, succ) and
|
||||
reachableFromDelegateCreation(pred)
|
||||
reachesDelegateCall(succ)
|
||||
}
|
||||
|
||||
private Expr delegateCallSource(Call c) {
|
||||
// Base case
|
||||
delegateCall(c, result)
|
||||
private Expr delegateCallSource(Callable c) {
|
||||
delegateCreation(result, c, _)
|
||||
or
|
||||
// Recursive case
|
||||
delegateFlowStepReachable(result, delegateCallSource(c))
|
||||
delegateFlowStepReaches(delegateCallSource(c), result)
|
||||
}
|
||||
|
||||
/** Gets a run-time target for the delegate call `c`. */
|
||||
Callable getARuntimeDelegateTarget(Call c) {
|
||||
delegateCreation(delegateCallSource(c), result, _)
|
||||
delegateCall(c, delegateCallSource(result))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,70 +6,5 @@
|
||||
import csharp
|
||||
|
||||
module TaintTracking {
|
||||
private import semmle.code.csharp.dataflow.DataFlow
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
* A taint tracking configuration is a special dataflow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values, but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* Each use of the taint tracking library must define its own unique extension
|
||||
* of this abstract class. A configuration defines a set of relevant sources
|
||||
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
|
||||
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
|
||||
* (`isAdditionalTaintStep()`).
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the intermediate node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `pred` to `succ`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -6,70 +6,5 @@
|
||||
import csharp
|
||||
|
||||
module TaintTracking2 {
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
* A taint tracking configuration is a special dataflow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values, but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* Each use of the taint tracking library must define its own unique extension
|
||||
* of this abstract class. A configuration defines a set of relevant sources
|
||||
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
|
||||
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
|
||||
* (`isAdditionalTaintStep()`).
|
||||
*/
|
||||
abstract class Configuration extends DataFlow2::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the intermediate node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `pred` to `succ`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -6,70 +6,5 @@
|
||||
import csharp
|
||||
|
||||
module TaintTracking3 {
|
||||
private import semmle.code.csharp.dataflow.DataFlow3
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
* A taint tracking configuration is a special dataflow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values, but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* Each use of the taint tracking library must define its own unique extension
|
||||
* of this abstract class. A configuration defines a set of relevant sources
|
||||
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
|
||||
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
|
||||
* (`isAdditionalTaintStep()`).
|
||||
*/
|
||||
abstract class Configuration extends DataFlow3::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the intermediate node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `pred` to `succ`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -6,70 +6,5 @@
|
||||
import csharp
|
||||
|
||||
module TaintTracking4 {
|
||||
private import semmle.code.csharp.dataflow.DataFlow4
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
* A taint tracking configuration is a special dataflow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values, but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* Each use of the taint tracking library must define its own unique extension
|
||||
* of this abstract class. A configuration defines a set of relevant sources
|
||||
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
|
||||
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
|
||||
* (`isAdditionalTaintStep()`).
|
||||
*/
|
||||
abstract class Configuration extends DataFlow4::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the intermediate node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `pred` to `succ`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.dataflow.internal.tainttracking4.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -6,70 +6,5 @@
|
||||
import csharp
|
||||
|
||||
module TaintTracking5 {
|
||||
private import semmle.code.csharp.dataflow.DataFlow5
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
* A taint tracking configuration is a special dataflow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values, but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* Each use of the taint tracking library must define its own unique extension
|
||||
* of this abstract class. A configuration defines a set of relevant sources
|
||||
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
|
||||
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
|
||||
* (`isAdditionalTaintStep()`).
|
||||
*/
|
||||
abstract class Configuration extends DataFlow5::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the intermediate node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `pred` to `succ`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
isAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.dataflow.internal.tainttracking5.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -87,6 +87,16 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
none()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprBase(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor,
|
||||
ControlFlow::Nodes::ElementNode cfn1, int i, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
|
||||
cfn1 = e1.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn1
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprRec(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor,
|
||||
@@ -108,9 +118,7 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor,
|
||||
ControlFlow::Nodes::ElementNode cfn1, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
|
||||
cfn1 = e1.getAControlFlowNode() and
|
||||
bb = cfn1.getBasicBlock()
|
||||
this.reachesBasicBlockExprBase(e1, e2, scope, exactScope, isSuccessor, cfn1, _, bb)
|
||||
or
|
||||
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
|
||||
exists(ControlFlowElement scope0, boolean exactScope0 |
|
||||
@@ -120,6 +128,16 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionBase(
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn, int i, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
|
||||
cfn = e.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionRec(
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
@@ -141,9 +159,7 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
|
||||
cfn = e.getAControlFlowNode() and
|
||||
bb = cfn.getBasicBlock()
|
||||
this.reachesBasicBlockDefinitionBase(e, def, scope, exactScope, isSuccessor, cfn, _, bb)
|
||||
or
|
||||
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
|
||||
exists(ControlFlowElement scope0, boolean exactScope0 |
|
||||
@@ -158,7 +174,18 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
|
||||
*/
|
||||
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
|
||||
exists(ControlFlow::BasicBlock bb | this.reachesBasicBlockExpr(e1, e2, _, _, _, cfn1, bb) |
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockExprBase(e1, e2, _, _, isSuccessor, cfn1, i, bb) and
|
||||
cfn2 = bb.getNode(j) and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
|
|
||||
isSuccessor = true and j > i
|
||||
or
|
||||
isSuccessor = false and i > j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockExprRec(e1, e2, _, _, _, cfn1, bb) and
|
||||
cfn2 = bb.getANode() and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
)
|
||||
@@ -171,7 +198,18 @@ abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
predicate hasDefPath(
|
||||
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock bb | this.reachesBasicBlockDefinition(e, def, _, _, _, cfn, bb) |
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockDefinitionBase(e, def, _, _, isSuccessor, cfn, i, bb) and
|
||||
cfnDef = bb.getNode(j) and
|
||||
def.getAControlFlowNode() = cfnDef
|
||||
|
|
||||
isSuccessor = true and j > i
|
||||
or
|
||||
isSuccessor = false and i > j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockDefinitionRec(e, def, _, _, _, cfn, bb) and
|
||||
def.getAControlFlowNode() = cfnDef and
|
||||
cfnDef = bb.getANode()
|
||||
)
|
||||
|
||||
@@ -107,7 +107,7 @@ private module Cached {
|
||||
|
||||
/** Gets a viable run-time target for the call `call`. */
|
||||
cached
|
||||
DotNet::Callable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() }
|
||||
DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() }
|
||||
|
||||
/**
|
||||
* Gets a viable run-time target for the delegate call `call`, requiring
|
||||
@@ -122,7 +122,7 @@ private module Cached {
|
||||
* targets of call `call` in `c`.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInCallContext(DataFlowCall call, DotNet::Callable c, DataFlowCall ctx) {
|
||||
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
|
||||
c = viableImpl(ctx) and
|
||||
c = call.getEnclosingCallable() and
|
||||
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
|
||||
@@ -153,7 +153,7 @@ private module Cached {
|
||||
* returned to.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInReturn(DotNet::Callable c, DataFlowCall call) {
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
c = viableImpl(call) and
|
||||
ctxtgts = strictcount(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
|
||||
@@ -168,7 +168,7 @@ private module Cached {
|
||||
* flow from the result to `call` restricts the possible context `ctx`.
|
||||
*/
|
||||
cached
|
||||
DotNet::Callable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
|
||||
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
reducedViableImplInReturn(result, call)
|
||||
}
|
||||
@@ -285,6 +285,8 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
|
||||
override string toString() { result = "captured " + v }
|
||||
}
|
||||
|
||||
class DataFlowCallable = DotNet::Callable;
|
||||
|
||||
/** A call relevant for data flow. */
|
||||
abstract class DataFlowCall extends TDataFlowCall {
|
||||
/**
|
||||
@@ -292,7 +294,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
* declaration, and if the callable has both CIL and source code, only
|
||||
* the source code version is returned.
|
||||
*/
|
||||
abstract DotNet::Callable getARuntimeTarget();
|
||||
abstract DataFlowCallable getARuntimeTarget();
|
||||
|
||||
/** Gets the control flow node where this call happens, if any. */
|
||||
abstract ControlFlow::Nodes::ElementNode getControlFlowNode();
|
||||
@@ -301,7 +303,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract DataFlow::Node getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
abstract DotNet::Callable getEnclosingCallable();
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets the underlying expression, if any. */
|
||||
final DotNet::Expr getExpr() { result = this.getNode().asExpr() }
|
||||
@@ -387,7 +389,7 @@ class ImplicitDelegateDataFlowCall extends DelegateDataFlowCall, TImplicitDelega
|
||||
* call `c` targeting a library callable. For example, `M` is the `0`th
|
||||
* argument of `new Lazy<int>(M)`.
|
||||
*/
|
||||
predicate isArgumentOf(DataFlowCall c, int i) {
|
||||
predicate isArgumentOf(NonDelegateDataFlowCall c, int i) {
|
||||
exists(ImplicitDelegateOutNode out | out.getControlFlowNode() = cfn | out.isArgumentOf(c, i))
|
||||
}
|
||||
|
||||
@@ -415,7 +417,7 @@ class ImplicitDelegateDataFlowCall extends DelegateDataFlowCall, TImplicitDelega
|
||||
* a single call for performance reasons.
|
||||
*/
|
||||
class TransitiveCapturedDataFlowCall extends DataFlowCall, TTransitiveCapturedCall {
|
||||
ControlFlow::Nodes::ElementNode cfn;
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
TransitiveCapturedDataFlowCall() { this = TTransitiveCapturedCall(cfn) }
|
||||
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -121,6 +121,27 @@ abstract class Configuration extends string {
|
||||
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate inBarrier(Node node, Configuration config) {
|
||||
config.isBarrierIn(node) and
|
||||
config.isSource(node)
|
||||
@@ -162,7 +183,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
localFlowStep(node1, node2) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
@@ -257,10 +278,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, config) and
|
||||
storeCandFwd1(f, config) and
|
||||
(stored = false or stored = true) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
@@ -287,9 +307,18 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, true, config) and
|
||||
read(mid, f, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
not fullBarrier(node, config) and
|
||||
@@ -337,10 +366,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand1(f, unbind(config)) and
|
||||
nodeCand1(mid, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, config) and
|
||||
readCand1(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -377,6 +405,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand1(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -387,6 +416,15 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCand1(mid, true, config) and
|
||||
storeCandFwd1(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throughFlowNodeCand(Node node, Configuration config) {
|
||||
nodeCand1(node, false, config) and
|
||||
not fullBarrier(node, config) and
|
||||
@@ -648,10 +686,9 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
storeCandFwd2(f, unbind(config)) and
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, config) and
|
||||
storeCandFwd2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -674,6 +711,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -684,6 +722,15 @@ private predicate storeCandFwd2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, true, config) and
|
||||
read(mid, f, node) and
|
||||
readCand1(f, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is part of a path from a source to a sink in the given
|
||||
* configuration taking simple call contexts into consideration.
|
||||
@@ -721,10 +768,9 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
store(node, f, mid) and
|
||||
readCand2(f, unbind(config)) and
|
||||
nodeCand2(mid, toReturn, true, config) and
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, config) and
|
||||
readCand2(f, config) and
|
||||
(stored = false or stored = true)
|
||||
)
|
||||
or
|
||||
@@ -755,6 +801,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate readCand2(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
@@ -765,16 +812,21 @@ private predicate readCand2(Content f, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node n1, Node n2 |
|
||||
store(n1, f, n2) and
|
||||
nodeCand2(n1, _, _, conf) and
|
||||
nodeCand2(n2, _, _, unbind(conf))
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid) and
|
||||
nodeCand2(mid, toReturn, true, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Content f, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, conf) and
|
||||
nodeCand2(node, _, _, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
@@ -783,7 +835,7 @@ private predicate readCand(Content f, Configuration conf) { readCand2(f, conf) }
|
||||
pragma[noinline]
|
||||
private predicate readStoreCand(Content f, Configuration conf) {
|
||||
storeCand(f, conf) and
|
||||
readCand(f, conf)
|
||||
readCand2(f, conf)
|
||||
}
|
||||
|
||||
private predicate nodeCand(Node node, Configuration config) { nodeCand2(node, _, _, config) }
|
||||
@@ -1009,15 +1061,13 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
exists(Node mid, Content f, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
read(mid, f, node) and
|
||||
nodeCand(node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCandFwd(f, apf, unbind(config))
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, config) and
|
||||
consCandFwd(f, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
@@ -1028,6 +1078,16 @@ private predicate consCandFwd(Content f, AccessPathFront apf, Configuration conf
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid, AccessPathFront apf |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
read(mid, f, node) and
|
||||
apf.headUsesContent(f) and
|
||||
nodeCand(node, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from a source to `node` with the given `apf` and
|
||||
* from there flow to a sink.
|
||||
@@ -1118,6 +1178,7 @@ private predicate flowCandRead(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
@@ -1127,6 +1188,7 @@ private predicate flowCandStore(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
consCandFwd(f, apf, config) and
|
||||
exists(Node n, AccessPathFront apf0 |
|
||||
@@ -1651,6 +1713,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
exists(Content f, AccessPath ap0 |
|
||||
ap0 = mid.getAp() and
|
||||
|
||||
@@ -35,7 +35,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlowNoCtx(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -152,7 +152,7 @@ private module ImplCommon {
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
localFlowStep(mid, node) and
|
||||
simpleLocalFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
@@ -209,7 +209,7 @@ private module ImplCommon {
|
||||
* through a value-preserving method.
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ private import DataFlowImplCommon
|
||||
private import ControlFlowReachability
|
||||
private import DelegateDataFlow
|
||||
private import semmle.code.csharp.Caching
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.frameworks.EntityFramework
|
||||
@@ -14,7 +15,7 @@ private import semmle.code.csharp.frameworks.NHibernate
|
||||
|
||||
/** Calculation of the relative order in which `this` references are read. */
|
||||
private module ThisFlow {
|
||||
class BasicBlock = ControlFlow::BasicBlock;
|
||||
private class BasicBlock = ControlFlow::BasicBlock;
|
||||
|
||||
/** Holds if `n` is a `this` access at control flow node `cfn`. */
|
||||
private predicate thisAccess(Node n, ControlFlow::Node cfn) {
|
||||
@@ -220,6 +221,54 @@ module LocalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument of a C# call. */
|
||||
private class Argument extends Expr {
|
||||
private Expr call;
|
||||
|
||||
private int arg;
|
||||
|
||||
Argument() {
|
||||
call = any(DispatchCall dc |
|
||||
this = dc.getArgument(arg)
|
||||
or
|
||||
this = dc.getQualifier() and arg = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
|
||||
).getCall()
|
||||
or
|
||||
this = call.(DelegateCall).getArgument(arg)
|
||||
}
|
||||
|
||||
/** Holds if this expression is the `i`th argument of `c`. */
|
||||
predicate isArgumentOf(Expr c, int i) { c = call and i = arg }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an assignment of `src` to a non-static field or field-like
|
||||
* property `f` of `q`.
|
||||
*/
|
||||
private predicate instanceFieldLikeAssign(Expr e, FieldLike f, Expr src, Expr q) {
|
||||
exists(FieldLikeAccess fa, AssignableDefinition def |
|
||||
def.getTargetAccess() = fa and
|
||||
f = fa.getTarget() and
|
||||
not f.isStatic() and
|
||||
src = def.getSource() and
|
||||
q = fa.getQualifier() and
|
||||
e = def.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `oc` has an object initializer that assigns `src` to non-static field or
|
||||
* field-like property `f`.
|
||||
*/
|
||||
private predicate instanceFieldLikeInit(ObjectCreation oc, FieldLike f, Expr src) {
|
||||
exists(MemberInitializer mi |
|
||||
mi = oc.getInitializer().(ObjectInitializer).getAMemberInitializer() and
|
||||
f = mi.getInitializedMember() and
|
||||
not f.isStatic() and
|
||||
src = mi.getRValue()
|
||||
)
|
||||
}
|
||||
|
||||
/** A collection of cached types and predicates to be evaluated in the same stage. */
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -247,14 +296,26 @@ private module Cached {
|
||||
cfn.getElement() instanceof DelegateArgumentToLibraryCallable and
|
||||
any(DelegateArgumentConfiguration x).hasExprPath(_, cfn, _, call)
|
||||
} or
|
||||
TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation }
|
||||
TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation } or
|
||||
TArgumentPostCallNode(ControlFlow::Nodes::ElementNode cfn) {
|
||||
exists(Argument a, Type t |
|
||||
a = cfn.getElement() and
|
||||
t = a.stripCasts().getType()
|
||||
|
|
||||
t instanceof RefType or
|
||||
t = any(TypeParameter tp | not tp.isValueType())
|
||||
)
|
||||
} or
|
||||
TStoreTargetNode(ControlFlow::Nodes::ElementNode cfn) {
|
||||
instanceFieldLikeAssign(_, _, _, cfn.getElement())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
any(LocalFlow::LocalExprStepConfiguration x).hasNodePath(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow from SSA definition to first read
|
||||
@@ -266,14 +327,18 @@ private module Cached {
|
||||
or
|
||||
// Flow from read to next read
|
||||
exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo |
|
||||
Ssa::Internal::adjacentReadPairSameVar(cfnFrom, cfnTo)
|
||||
|
|
||||
nodeFrom = TExprNode(cfnFrom) and
|
||||
Ssa::Internal::adjacentReadPairSameVar(cfnFrom, cfnTo) and
|
||||
nodeTo = TExprNode(cfnTo)
|
||||
|
|
||||
nodeFrom = TExprNode(cfnFrom)
|
||||
or
|
||||
cfnFrom = nodeFrom.(PostUpdateNode).getPreUpdateNode().getControlFlowNode()
|
||||
)
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
or
|
||||
// Flow into SSA pseudo definition
|
||||
exists(Ssa::Definition def, Ssa::PseudoDefinition pseudo |
|
||||
LocalFlow::localFlowSsaInput(nodeFrom, def)
|
||||
@@ -308,6 +373,39 @@ private module Cached {
|
||||
predicate jumpStepImpl(ExprNode pred, ExprNode succ) {
|
||||
pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TContent = TFieldLikeContent(FieldLike f) { not f.isStatic() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to
|
||||
* content `c`.
|
||||
*/
|
||||
cached
|
||||
predicate storeStepImpl(ExprNode node1, Content c, PostUpdateNode node2) {
|
||||
exists(StoreStepConfiguration x, Node preNode2 |
|
||||
preNode2 = node2.getPreUpdateNode() and
|
||||
x.hasNodePath(node1, preNode2) and
|
||||
instanceFieldLikeAssign(_, c.(FieldLikeContent).getField(), node1.asExpr(), preNode2.asExpr())
|
||||
)
|
||||
or
|
||||
exists(StoreStepConfiguration x |
|
||||
x.hasNodePath(node1, node2) and
|
||||
instanceFieldLikeInit(node2.(ObjectCreationNode).getExpr(), c.(FieldLikeContent).getField(),
|
||||
node1.asExpr())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
|
||||
*/
|
||||
cached
|
||||
predicate readStepImpl(Node node1, Content c, Node node2) {
|
||||
exists(ReadStepConfiguration x |
|
||||
x.hasNodePath(node1, node2) and
|
||||
c.(FieldLikeContent).getField() = node2.asExpr().(FieldLikeRead).getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
import Cached
|
||||
|
||||
@@ -359,7 +457,7 @@ private module ParameterNodes {
|
||||
|
||||
override DotNet::Parameter getParameter() { result = parameter }
|
||||
|
||||
override predicate isParameterOf(DotNet::Callable c, int i) { c.getParameter(i) = parameter }
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter }
|
||||
|
||||
override DotNet::Callable getEnclosingCallable() { result = parameter.getCallable() }
|
||||
|
||||
@@ -379,7 +477,7 @@ private module ParameterNodes {
|
||||
/** Gets the callable containing this implicit instance parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
|
||||
override predicate isParameterOf(DotNet::Callable c, int pos) { callable = c and pos = -1 }
|
||||
override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 }
|
||||
|
||||
override Callable getEnclosingCallable() { result = callable }
|
||||
|
||||
@@ -406,7 +504,7 @@ private module ParameterNodes {
|
||||
// `getParameter()` is explicitly *not* overriden to return `parameter`,
|
||||
// as that would otherwise enable tainted parameters to accidentally be
|
||||
// used by users of the library
|
||||
override predicate isParameterOf(DotNet::Callable c, int i) {
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) {
|
||||
c = parameter.getCallable() and
|
||||
// we model tainted parameters as if they had been extra parameters after
|
||||
// the actual parameters
|
||||
@@ -483,7 +581,7 @@ private module ParameterNodes {
|
||||
/** Gets the captured variable that this implicit parameter models. */
|
||||
LocalScopeVariable getVariable() { result = def.getVariable() }
|
||||
|
||||
override predicate isParameterOf(DotNet::Callable c, int i) {
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) {
|
||||
i = getParameterPosition(def) and
|
||||
c = this.getEnclosingCallable()
|
||||
}
|
||||
@@ -491,6 +589,16 @@ private module ParameterNodes {
|
||||
}
|
||||
import ParameterNodes
|
||||
|
||||
/** A data flow node that represents a call argument. */
|
||||
abstract class ArgumentNode extends Node {
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
cached
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
final DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private module ArgumentNodes {
|
||||
class DelegateArgumentConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
DelegateArgumentConfiguration() { this = "DelegateArgumentConfiguration" }
|
||||
@@ -508,9 +616,9 @@ private module ArgumentNodes {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is an argument of the call `call`, which resolves to a
|
||||
* library callable that is known to forward `arg` into the `i`th parameter
|
||||
* of a supplied delegate `delegate`.
|
||||
* Holds if `arg` is an argument of a call, which resolves to a library callable
|
||||
* that is known to forward `arg` into the `i`th parameter of a supplied
|
||||
* delegate `delegate`.
|
||||
*
|
||||
* For example, in
|
||||
*
|
||||
@@ -521,9 +629,9 @@ private module ArgumentNodes {
|
||||
* `arg = x`, `i = 0`, `call = x.Select(Foo)`, and `delegate = Foo`.
|
||||
*/
|
||||
private predicate flowIntoCallableLibraryCall(
|
||||
DataFlowCall call, Node arg, ImplicitDelegateDataFlowCall delegate, int i
|
||||
ExplicitArgumentNode arg, ImplicitDelegateDataFlowCall delegate, int i
|
||||
) {
|
||||
exists(int j, boolean preservesValue |
|
||||
exists(DataFlowCall call, int j, boolean preservesValue |
|
||||
preservesValue = true and i = j
|
||||
or
|
||||
preservesValue = false and i = j + delegate.getNumberOfDelegateParameters()
|
||||
@@ -548,68 +656,47 @@ private module ArgumentNodes {
|
||||
)
|
||||
}
|
||||
|
||||
private DotNet::Expr getArgument(DotNet::Expr call, int i) {
|
||||
call = any(DispatchCall dc |
|
||||
result = dc.getArgument(i)
|
||||
or
|
||||
result = dc.getQualifier() and i = -1 and not dc.getAStaticTarget().(Modifiable).isStatic()
|
||||
).getCall()
|
||||
or
|
||||
result = call.(DelegateCall).getArgument(i)
|
||||
or
|
||||
result = call.(CIL::Call).getArgument(i)
|
||||
}
|
||||
|
||||
private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
ArgumentConfiguration() { this = "ArgumentConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
e1 = getArgument(e2, _) and
|
||||
e1.(Argument).isArgumentOf(e2, _) and
|
||||
exactScope = false and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call argument. */
|
||||
abstract class ArgumentNode extends Node {
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
cached
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
final DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A data flow node that represents an explicit call argument. */
|
||||
private class ExplicitArgumentNode extends ArgumentNode {
|
||||
private DataFlowCall c;
|
||||
|
||||
private int i;
|
||||
|
||||
ExplicitArgumentNode() {
|
||||
exists(DotNet::Expr e, DotNet::Expr arg |
|
||||
arg = this.asExpr() and
|
||||
e = c.getExpr() and
|
||||
arg = getArgument(e, i)
|
||||
|
|
||||
any(ArgumentConfiguration x)
|
||||
.hasExprPath(_, this.getControlFlowNode(), _, c.getControlFlowNode())
|
||||
or
|
||||
// No CFG splitting in CIL
|
||||
e instanceof CIL::Expr and
|
||||
arg instanceof CIL::Expr
|
||||
)
|
||||
this.asExpr() instanceof Argument
|
||||
or
|
||||
flowIntoCallableLibraryCall(_, this, c, i)
|
||||
this.asExpr() = any(CIL::Call call).getAnArgument()
|
||||
or
|
||||
libraryFlowDelegateCallIn(_, _, this.asExpr(), _, _, _)
|
||||
or
|
||||
this.(ImplicitDelegateOutNode).isArgumentOf(_, _)
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
call = c and
|
||||
pos = i
|
||||
exists(ArgumentConfiguration x, Expr c, Argument arg |
|
||||
arg = this.asExpr() and
|
||||
c = call.getExpr() and
|
||||
arg.isArgumentOf(c, pos) and
|
||||
x.hasExprPath(_, this.getControlFlowNode(), _, call.getControlFlowNode())
|
||||
)
|
||||
or
|
||||
exists(CIL::Call c, CIL::Expr arg |
|
||||
arg = this.asExpr() and
|
||||
c = call.getExpr() and
|
||||
arg = c.getArgument(pos)
|
||||
)
|
||||
or
|
||||
flowIntoCallableLibraryCall(this, call, pos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,13 +791,13 @@ private module ArgumentNodes {
|
||||
}
|
||||
import ArgumentNodes
|
||||
|
||||
private module ReturnNodes {
|
||||
/** A data flow node that represents a value returned by a callable. */
|
||||
abstract class ReturnNode extends Node {
|
||||
/** Gets the kind of this return node. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
/** A data flow node that represents a value returned by a callable. */
|
||||
abstract class ReturnNode extends Node {
|
||||
/** Gets the kind of this return node. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
private module ReturnNodes {
|
||||
/**
|
||||
* A data flow node that represents an expression returned by a callable,
|
||||
* either using a (`yield`) `return` statement or an expression body (`=>`).
|
||||
@@ -817,14 +904,14 @@ private module ReturnNodes {
|
||||
}
|
||||
import ReturnNodes
|
||||
|
||||
private module OutNodes {
|
||||
/** A data flow node that represents the output of a call. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call. */
|
||||
cached
|
||||
abstract DataFlowCall getCall();
|
||||
}
|
||||
/** A data flow node that represents the output of a call. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call. */
|
||||
cached
|
||||
abstract DataFlowCall getCall();
|
||||
}
|
||||
|
||||
private module OutNodes {
|
||||
private DataFlowCall csharpCall(Expr e, ControlFlow::Node cfn) {
|
||||
e = any(DispatchCall dc | result = TNonDelegateCall(cfn, dc)).getCall() or
|
||||
result = TExplicitDelegateCall(cfn, e)
|
||||
@@ -988,14 +1075,23 @@ predicate flowOutOfDelegateLibraryCall(
|
||||
private class FieldLike extends Assignable, Modifiable {
|
||||
FieldLike() {
|
||||
this instanceof Field or
|
||||
this = any(TrivialProperty p | not p.isOverridableOrImplementable())
|
||||
this = any(Property p |
|
||||
not p.isOverridableOrImplementable() and
|
||||
(
|
||||
p.isAutoImplemented()
|
||||
or
|
||||
p.matchesHandle(any(CIL::TrivialProperty tp))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class FieldLikeRead extends AssignableRead {
|
||||
FieldLikeRead() { this.getTarget() instanceof FieldLike }
|
||||
private class FieldLikeAccess extends AssignableAccess, QualifiableExpr {
|
||||
FieldLikeAccess() { this.getTarget() instanceof FieldLike }
|
||||
}
|
||||
|
||||
private class FieldLikeRead extends FieldLikeAccess, AssignableRead { }
|
||||
|
||||
/**
|
||||
* Holds if the field-like read `flr` is not completely determined by explicit
|
||||
* SSA updates.
|
||||
@@ -1035,41 +1131,73 @@ private class StaticFieldLikeJumpNode extends NonLocalJumpNode, ExprNode {
|
||||
|
||||
predicate jumpStep = jumpStepImpl/2;
|
||||
|
||||
private newtype TContent = TContentStub()
|
||||
|
||||
/** A reference contained in an object. A stub implementation, for now. */
|
||||
// stub implementation
|
||||
/**
|
||||
* A reference contained in an object. Currently limited to instance fields
|
||||
* and field-like instance properties.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "stub" }
|
||||
/** Gets a textual representation of this content. */
|
||||
abstract string toString();
|
||||
|
||||
Location getLocation() { result instanceof EmptyLocation }
|
||||
abstract Location getLocation();
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
RefType getContainerType() { none() }
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
Type getType() { none() }
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
none() // stub implementation
|
||||
private class FieldLikeContent extends Content, TFieldLikeContent {
|
||||
private FieldLike f;
|
||||
|
||||
FieldLikeContent() { this = TFieldLikeContent(f) }
|
||||
|
||||
FieldLike getField() { result = f }
|
||||
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
override Location getLocation() { result = f.getLocation() }
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
none() // stub implementation
|
||||
private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
StoreStepConfiguration() { this = "StoreStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
isSuccessor = false and
|
||||
instanceFieldLikeAssign(scope, _, e1, e2)
|
||||
or
|
||||
exactScope = false and
|
||||
isSuccessor = false and
|
||||
instanceFieldLikeInit(e2, _, e1) and
|
||||
scope = e2
|
||||
}
|
||||
}
|
||||
|
||||
predicate storeStep = storeStepImpl/3;
|
||||
|
||||
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
ReadStepConfiguration() { this = "ReadStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
e1 = e2.(FieldLikeRead).getQualifier() and
|
||||
scope = e2
|
||||
}
|
||||
}
|
||||
|
||||
predicate readStep = readStepImpl/3;
|
||||
|
||||
private predicate suppressUnusedType(DotNet::Type t) { any() }
|
||||
|
||||
/**
|
||||
@@ -1078,7 +1206,10 @@ private predicate suppressUnusedType(DotNet::Type t) { any() }
|
||||
* Type-based pruning is disabled for now, so this is a stub implementation.
|
||||
*/
|
||||
bindingset[t]
|
||||
Type getErasedRepr(DotNet::Type t) { suppressUnusedType(t) and result instanceof ObjectType } // stub implementation
|
||||
DotNet::Type getErasedRepr(DotNet::Type t) {
|
||||
// stub implementation
|
||||
suppressUnusedType(t) and result instanceof ObjectType
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
bindingset[t]
|
||||
@@ -1102,21 +1233,62 @@ predicate compatibleTypes(DotNet::Type t1, DotNet::Type t2) {
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ObjectCreation`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
class PostUpdateNode extends Node {
|
||||
PostUpdateNode() { none() } // stub implementation
|
||||
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/** Gets the node before the state update. */
|
||||
Node getPreUpdateNode() { none() } // stub implementation
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
private module PostUpdateNodes {
|
||||
class ObjectCreationNode extends PostUpdateNode, ExprNode, TExprNode {
|
||||
ObjectCreationNode() { exists(ObjectCreation oc | this = TExprNode(oc.getAControlFlowNode())) }
|
||||
|
||||
override MallocNode getPreUpdateNode() { this = TExprNode(result.getControlFlowNode()) }
|
||||
}
|
||||
|
||||
private class ArgumentPostCallNode extends PostUpdateNode, TArgumentPostCallNode {
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
ArgumentPostCallNode() { this = TArgumentPostCallNode(cfn) }
|
||||
|
||||
override ExprNode getPreUpdateNode() { cfn = result.getControlFlowNode() }
|
||||
|
||||
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
|
||||
override Type getType() { result = cfn.getElement().(Expr).getType() }
|
||||
|
||||
override Location getLocation() { result = cfn.getLocation() }
|
||||
|
||||
override string toString() { result = "[post] " + cfn.toString() }
|
||||
}
|
||||
|
||||
private class StoreTargetNode extends PostUpdateNode, TStoreTargetNode {
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
StoreTargetNode() { this = TStoreTargetNode(cfn) }
|
||||
|
||||
override ExprNode getPreUpdateNode() { cfn = result.getControlFlowNode() }
|
||||
|
||||
override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
|
||||
override Type getType() { result = cfn.getElement().(Expr).getType() }
|
||||
|
||||
override Location getLocation() { result = cfn.getLocation() }
|
||||
|
||||
override string toString() { result = "[post] " + cfn.toString() }
|
||||
}
|
||||
}
|
||||
private import PostUpdateNodes
|
||||
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends ExprNode {
|
||||
CastNode() { this.getExpr() instanceof CastExpr }
|
||||
}
|
||||
|
||||
class DataFlowCallable = DotNet::Callable;
|
||||
|
||||
class DataFlowExpr = DotNet::Expr;
|
||||
|
||||
class DataFlowType = DotNet::Type;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import csharp
|
||||
private import cil
|
||||
private import dotnet
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.csharp.Caching
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
@@ -34,7 +35,7 @@ class Node extends TNode {
|
||||
|
||||
/** Gets the enclosing callable of this node. */
|
||||
cached
|
||||
DotNet::Callable getEnclosingCallable() { none() }
|
||||
DataFlowCallable getEnclosingCallable() { none() }
|
||||
|
||||
/** Gets the control flow node corresponding to this node, if any. */
|
||||
cached
|
||||
@@ -88,7 +89,7 @@ class ExprNode extends Node {
|
||||
result = cfn.getElement()
|
||||
}
|
||||
|
||||
override DotNet::Callable getEnclosingCallable() {
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.getExpr().getEnclosingCallable()
|
||||
}
|
||||
@@ -136,7 +137,7 @@ class ParameterNode extends Node {
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* (zero-based) position.
|
||||
*/
|
||||
predicate isParameterOf(DotNet::Callable c, int i) { none() }
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { none() }
|
||||
}
|
||||
|
||||
/** Gets a node corresponding to expression `e`. */
|
||||
@@ -147,7 +148,11 @@ ExprNode exprNode(DotNet::Expr e) { result.getExpr() = e }
|
||||
*/
|
||||
ParameterNode parameterNode(DotNet::Parameter p) { result.getParameter() = p }
|
||||
|
||||
predicate localFlowStep = localFlowStepImpl/2;
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
|
||||
/**
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import csharp
|
||||
private import csharp
|
||||
private import TaintTrackingPublic
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
@@ -10,6 +10,22 @@ private import semmle.code.csharp.frameworks.JsonNET
|
||||
private import cil
|
||||
private import dotnet
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a barrier in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
localAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
}
|
||||
|
||||
private CIL::DataFlowNode asCilDataFlowNode(DataFlow::Node node) {
|
||||
result = node.asParameter() or
|
||||
result = node.asExpr()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import csharp
|
||||
private import csharp
|
||||
private import TaintTrackingPrivate
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow as DataFlow
|
||||
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user