mirror of
https://github.com/github/codeql.git
synced 2026-05-02 12:15:17 +02:00
Merge branch 'master' of github.com:Semmle/ql into attribute
This commit is contained in:
@@ -57,5 +57,7 @@ predicate declaredInLoop(LocalVariableDecl v, LoopStmt loop) {
|
||||
from Assignment a, Variable v
|
||||
where
|
||||
useAndDef(a, v) and
|
||||
exists(LoopStmt loop | a.getEnclosingStmt().getEnclosingStmt*() = loop | not declaredInLoop(v, loop))
|
||||
exists(LoopStmt loop | a.getEnclosingStmt().getEnclosingStmt*() = loop |
|
||||
not declaredInLoop(v, loop)
|
||||
)
|
||||
select a, "The string " + v.getName() + " is built-up in a loop: use string buffer."
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import semmle.code.java.security.XSS
|
||||
import DataFlow2::PathGraph
|
||||
|
||||
class XSSConfig extends TaintTracking::Configuration2 {
|
||||
class XSSConfig extends TaintTracking2::Configuration {
|
||||
XSSConfig() { this = "XSSConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import semmle.code.java.security.XSS
|
||||
import DataFlow2::PathGraph
|
||||
|
||||
class XSSLocalConfig extends TaintTracking::Configuration2 {
|
||||
class XSSLocalConfig extends TaintTracking2::Configuration {
|
||||
XSSLocalConfig() { this = "XSSLocalConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import semmle.code.java.security.XSS
|
||||
|
||||
/**
|
||||
@@ -80,7 +81,7 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||
)
|
||||
}
|
||||
|
||||
class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration2 {
|
||||
class StackTraceStringToXssSinkFlowConfig extends TaintTracking2::Configuration {
|
||||
StackTraceStringToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
|
||||
}
|
||||
@@ -119,7 +120,7 @@ class GetMessageFlowSource extends MethodAccess {
|
||||
}
|
||||
}
|
||||
|
||||
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration2 {
|
||||
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking2::Configuration {
|
||||
GetMessageFlowSourceToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
import java
|
||||
import XmlParsers
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class SafeSAXSourceFlowConfig extends TaintTracking::Configuration2 {
|
||||
class SafeSAXSourceFlowConfig extends TaintTracking2::Configuration {
|
||||
SafeSAXSourceFlowConfig() { this = "XmlParsers::SafeSAXSourceFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXSource }
|
||||
|
||||
@@ -1117,9 +1117,7 @@ class SwitchExpr extends Expr, @switchexpr {
|
||||
Expr getAResult() {
|
||||
result = getACase().getRuleExpression()
|
||||
or
|
||||
exists(BreakStmt break |
|
||||
break.(JumpStmt).getTarget() = this and result = break.getValue()
|
||||
)
|
||||
exists(BreakStmt break | break.(JumpStmt).getTarget() = this and result = break.getValue())
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
|
||||
@@ -120,9 +120,7 @@ class Callable extends StmtParent, Member, @callable {
|
||||
*
|
||||
* This includes both static call targets and dynamic dispatch targets.
|
||||
*/
|
||||
predicate polyCalls(Callable m) {
|
||||
this.calls(m) or this.callsImpl(m)
|
||||
}
|
||||
predicate polyCalls(Callable m) { this.calls(m) or this.callsImpl(m) }
|
||||
|
||||
/**
|
||||
* Holds if `c` is a viable implementation of a callable called by this
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* `com.google.common.base.Preconditions` and
|
||||
* `org.apache.commons.lang3.Validate`.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,23 +7,4 @@ import java
|
||||
|
||||
module DataFlow {
|
||||
import semmle.code.java.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.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
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 java
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.java.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`.
|
||||
*/
|
||||
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 java
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.java.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`.
|
||||
*/
|
||||
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 java
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.java.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`.
|
||||
*/
|
||||
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 java
|
||||
|
||||
module DataFlow5 {
|
||||
import semmle.code.java.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.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,802 +3,16 @@
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.Collections
|
||||
private import SSA
|
||||
private import DefUse
|
||||
private import semmle.code.java.security.SecurityTests
|
||||
private import semmle.code.java.security.Validation
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
private import semmle.code.java.Maps
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule
|
||||
|
||||
module TaintTracking {
|
||||
/**
|
||||
* 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 node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
}
|
||||
|
||||
/** 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
|
||||
localAdditionalTaintStep(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)
|
||||
}
|
||||
}
|
||||
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
private import semmle.code.java.dataflow.TaintTracking2
|
||||
|
||||
/**
|
||||
* 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()`).
|
||||
* DEPRECATED: Use TaintTracking2::Configuration instead.
|
||||
*/
|
||||
abstract class Configuration2 extends DataFlow2::Configuration {
|
||||
bindingset[this]
|
||||
Configuration2() { 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
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
}
|
||||
|
||||
/** 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
|
||||
localAdditionalTaintStep(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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink`.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
DataFlow::localFlowStep(src, sink) or
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
|
||||
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(ArrayCreationExpr).getInit() = src
|
||||
or
|
||||
sink.(ArrayInit).getAnInit() = src
|
||||
or
|
||||
sink.(ArrayAccess).getArray() = src
|
||||
or
|
||||
sink.(LogicExpr).getAnOperand() = src
|
||||
or
|
||||
exists(Assignment assign | assign.getSource() = src |
|
||||
sink = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
or
|
||||
exists(EnhancedForStmt for, SsaExplicitUpdate v |
|
||||
for.getExpr() = src and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = sink
|
||||
)
|
||||
or
|
||||
containerStep(src, sink)
|
||||
or
|
||||
constructorStep(src, sink)
|
||||
or
|
||||
qualifierToMethodStep(src, sink)
|
||||
or
|
||||
qualifierToArgumentStep(src, sink)
|
||||
or
|
||||
argToMethodStep(src, sink)
|
||||
or
|
||||
argToArgStep(src, sink)
|
||||
or
|
||||
argToQualifierStep(src, sink)
|
||||
or
|
||||
comparisonStep(src, sink)
|
||||
or
|
||||
stringBuilderStep(src, sink)
|
||||
or
|
||||
serializationStep(src, sink)
|
||||
}
|
||||
|
||||
private class BulkData extends RefType {
|
||||
BulkData() {
|
||||
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
|
||||
or
|
||||
exists(RefType t | this.getASourceSupertype*() = t |
|
||||
t.hasQualifiedName("java.io", "InputStream") or
|
||||
t.hasQualifiedName("java.nio", "ByteBuffer") or
|
||||
t.hasQualifiedName("java.lang", "Readable") or
|
||||
t.hasQualifiedName("java.io", "DataInput") or
|
||||
t.hasQualifiedName("java.nio.channels", "ReadableByteChannel")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a constructor for a subclass of `java.io.InputStream` that
|
||||
* wraps an underlying data source. The underlying data source is given as a
|
||||
* the `argi`'th parameter to the constructor.
|
||||
*
|
||||
* An object construction of such a wrapper is likely to preserve the data flow
|
||||
* status of its argument.
|
||||
*/
|
||||
private predicate inputStreamWrapper(Constructor c, int argi) {
|
||||
c.getParameterType(argi) instanceof BulkData and
|
||||
c.getDeclaringType().getASourceSupertype().hasQualifiedName("java.io", "InputStream")
|
||||
}
|
||||
|
||||
/** An object construction that preserves the data flow status of any of its arguments. */
|
||||
private predicate constructorStep(Expr tracked, ConstructorCall sink) {
|
||||
exists(int argi | sink.getArgument(argi) = tracked |
|
||||
exists(string s | sink.getConstructedType().getQualifiedName() = s |
|
||||
// String constructor does nothing to data
|
||||
s = "java.lang.String" and argi = 0
|
||||
or
|
||||
// some readers preserve the content of streams
|
||||
s = "java.io.InputStreamReader" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedReader" and argi = 0
|
||||
or
|
||||
s = "java.io.CharArrayReader" and argi = 0
|
||||
or
|
||||
s = "java.io.StringReader" and argi = 0
|
||||
or
|
||||
// data preserved through streams
|
||||
s = "java.io.ObjectInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.ByteArrayInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.DataInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedInputStream" and argi = 0
|
||||
or
|
||||
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
|
||||
or
|
||||
s = "java.beans.XMLDecoder" and argi = 0
|
||||
or
|
||||
// a tokenizer preserves the content of a string
|
||||
s = "java.util.StringTokenizer" and argi = 0
|
||||
or
|
||||
// unzipping the stream preserves content
|
||||
s = "java.util.zip.ZipInputStream" and argi = 0
|
||||
or
|
||||
s = "java.util.zip.GZIPInputStream" and argi = 0
|
||||
or
|
||||
// string builders and buffers
|
||||
s = "java.lang.StringBuilder" and argi = 0
|
||||
or
|
||||
s = "java.lang.StringBuffer" and argi = 0
|
||||
or
|
||||
// a cookie with tainted ingredients is tainted
|
||||
s = "javax.servlet.http.Cookie" and argi = 0
|
||||
or
|
||||
s = "javax.servlet.http.Cookie" and argi = 1
|
||||
or
|
||||
// various xml stream source constructors.
|
||||
s = "org.xml.sax.InputSource" and argi = 0
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
|
||||
or
|
||||
s = "javax.xml.transform.stream.StreamSource" and argi = 0
|
||||
or
|
||||
//a URI constructed from a tainted string is tainted.
|
||||
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
|
||||
)
|
||||
or
|
||||
exists(RefType t | t.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(t, sink.getConstructedType())
|
||||
) and
|
||||
argi = 0
|
||||
or
|
||||
// wrappers constructed by extension
|
||||
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
|
||||
c = sink.getConstructor() and
|
||||
p = c.getParameter(argi) and
|
||||
sup.getEnclosingCallable() = c and
|
||||
constructorStep(p.getAnAccess(), sup)
|
||||
)
|
||||
or
|
||||
// a custom InputStream that wraps a tainted data source is tainted
|
||||
inputStreamWrapper(sink.getConstructor(), argi)
|
||||
)
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from qualifier to argument. */
|
||||
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma, int arg |
|
||||
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
|
||||
tracked = ma.getQualifier() and
|
||||
sink = ma.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
/** Methods that passes tainted data from qualifier to argument. */
|
||||
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
m.hasName("writeTo") and
|
||||
arg = 0
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
|
||||
m.hasName("read") and
|
||||
arg = 0
|
||||
or
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
|
||||
m.hasName("read") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from the qualifier. */
|
||||
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
|
||||
tracked = sink.getQualifier()
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods that return tainted data when called on tainted data.
|
||||
*/
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
m.getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
m.getName() = "concat" or
|
||||
m.getName() = "endsWith" or
|
||||
m.getName() = "getBytes" or
|
||||
m.getName() = "split" or
|
||||
m.getName() = "substring" or
|
||||
m.getName() = "toCharArray" or
|
||||
m.getName() = "toLowerCase" or
|
||||
m.getName() = "toString" or
|
||||
m.getName() = "toUpperCase" or
|
||||
m.getName() = "trim"
|
||||
)
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(c, m.getDeclaringType())
|
||||
) and
|
||||
(
|
||||
m.getName().matches("to%String") or
|
||||
m.getName() = "toByteArray" or
|
||||
m.getName().matches("%Value")
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
|
||||
(
|
||||
m.getName() = "read" and m.getNumberOfParameters() = 0
|
||||
or
|
||||
m.getName() = "readLine"
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
|
||||
m.getName() = "toString"
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
|
||||
m.getName().matches("next%")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
(m.getName() = "toByteArray" or m.getName() = "toString")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and
|
||||
m.getName().matches("read%")
|
||||
or
|
||||
(
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
) and
|
||||
(m.getName() = "toString" or m.getName() = "append")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
|
||||
m.hasName("getInputSource")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
|
||||
m.hasName("getInputStream")
|
||||
or
|
||||
m instanceof IntentGetExtraMethod
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
|
||||
m.hasName("get")
|
||||
or
|
||||
m = any(GuiceProvider gp).getAnOverridingGetMethod()
|
||||
or
|
||||
m = any(ProtobufMessageLite p).getAGetterMethod()
|
||||
}
|
||||
|
||||
private class StringReplaceMethod extends Method {
|
||||
StringReplaceMethod() {
|
||||
getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
hasName("replace") or
|
||||
hasName("replaceAll") or
|
||||
hasName("replaceFirst")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unsafeEscape(MethodAccess ma) {
|
||||
// Removing `<script>` tags using a string-replace method is
|
||||
// unsafe if such a tag is embedded inside another one (e.g. `<scr<script>ipt>`).
|
||||
exists(StringReplaceMethod m | ma.getMethod() = m |
|
||||
ma.getArgument(0).(StringLiteral).getRepresentedString() = "(<script>)" and
|
||||
ma.getArgument(1).(StringLiteral).getRepresentedString() = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from an argument. */
|
||||
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
exists(Method m, int i |
|
||||
m = sink.(MethodAccess).getMethod() and
|
||||
taintPreservingArgumentToMethod(m, i) and
|
||||
tracked = sink.(MethodAccess).getArgument(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that return tainted data if its
|
||||
* `arg`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
method instanceof StringReplaceMethod and arg = 1
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(c, method.getDeclaringType())
|
||||
) and
|
||||
(
|
||||
method.getName().matches("parse%") and arg = 0
|
||||
or
|
||||
method.getName().matches("valueOf%") and arg = 0
|
||||
or
|
||||
method.getName().matches("to%String") and arg = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType() instanceof TypeString and
|
||||
method.getName() = "concat" and
|
||||
arg = 0
|
||||
or
|
||||
(
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
) and
|
||||
(
|
||||
method.getName() = "append" and arg = 0
|
||||
or
|
||||
method.getName() = "insert" and arg = 1
|
||||
or
|
||||
method.getName() = "replace" and arg = 2
|
||||
)
|
||||
or
|
||||
(
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
|
||||
) and
|
||||
(
|
||||
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "encodeToString" and arg = 0
|
||||
or
|
||||
method.getName() = "wrap" and arg = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.getName() = "buffer" and arg = 0
|
||||
or
|
||||
method.getName() = "readLines" and arg = 0
|
||||
or
|
||||
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.getName() = "toBufferedInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toBufferedReader" and arg = 0
|
||||
or
|
||||
method.getName() = "toByteArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toCharArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toString" and arg = 0
|
||||
)
|
||||
or
|
||||
// A URI created from a tainted string is still tainted.
|
||||
method.getDeclaringType().hasQualifiedName("java.net", "URI") and
|
||||
method.hasName("create") and
|
||||
arg = 0
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
|
||||
method.hasName("sourceToInputSource") and
|
||||
arg = 0
|
||||
or
|
||||
exists(ProtobufParser p | method = p.getAParseFromMethod()) and
|
||||
arg = 0
|
||||
or
|
||||
exists(ProtobufMessageLite m | method = m.getAParseFromMethod()) and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
|
||||
* between arguments.
|
||||
*/
|
||||
private predicate argToArgStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma, Method method, int input, int output |
|
||||
taintPreservingArgToArg(method, input, output) and
|
||||
ma.getMethod() = method and
|
||||
ma.getArgument(input) = tracked and
|
||||
ma.getArgument(output) = sink
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that writes tainted data to the
|
||||
* `output`th argument if the `input`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgToArg(Method method, int input, int output) {
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.hasName("copy") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("copyLarge") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("read") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("readFully") and
|
||||
input = 0 and
|
||||
output = 1 and
|
||||
not method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.hasName("write") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeChunked") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeLines") and input = 0 and output = 2
|
||||
or
|
||||
method.hasName("writeLines") and input = 1 and output = 2
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
|
||||
method.hasName("arraycopy") and
|
||||
input = 0 and
|
||||
output = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tracked` is the argument of a method that transfers taint
|
||||
* from the argument to the qualifier and `sink` is the qualifier.
|
||||
*/
|
||||
private predicate argToQualifierStep(Expr tracked, Expr sink) {
|
||||
exists(Method m, int i, MethodAccess ma |
|
||||
taintPreservingArgumentToQualifier(m, i) and
|
||||
ma.getMethod() = m and
|
||||
tracked = ma.getArgument(i) and
|
||||
sink = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a method that transfers taint from argument to qualifier and
|
||||
* `arg` is the index of the argument.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
|
||||
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
method.hasName("write") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/** A comparison or equality test with a constant. */
|
||||
private predicate comparisonStep(Expr tracked, Expr sink) {
|
||||
exists(Expr other |
|
||||
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof EqualityTest |
|
||||
e = sink and
|
||||
e.hasOperands(tracked, other)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess m | m.getMethod() instanceof EqualsMethod |
|
||||
m = sink and
|
||||
(
|
||||
m.getQualifier() = tracked and m.getArgument(0) = other
|
||||
or
|
||||
m.getQualifier() = other and m.getArgument(0) = tracked
|
||||
)
|
||||
)
|
||||
|
|
||||
other.isCompileTimeConstant() or other instanceof NullLiteral
|
||||
)
|
||||
}
|
||||
|
||||
/** Flow through a `StringBuilder`. */
|
||||
private predicate stringBuilderStep(Expr tracked, Expr sink) {
|
||||
exists(StringBuilderVar sbvar, MethodAccess input, int arg |
|
||||
input = sbvar.getAnInput(arg) and
|
||||
tracked = input.getArgument(arg) and
|
||||
sink = sbvar.getToStringCall()
|
||||
)
|
||||
}
|
||||
|
||||
/** Flow through data serialization. */
|
||||
private predicate serializationStep(Expr tracked, Expr sink) {
|
||||
exists(ObjectOutputStreamVar v, VariableAssign def |
|
||||
def = v.getADef() and
|
||||
exists(MethodAccess ma, RValue use |
|
||||
ma.getArgument(0) = tracked and
|
||||
ma = v.getAWriteObjectMethodAccess() and
|
||||
use = ma.getQualifier() and
|
||||
defUsePair(def, use)
|
||||
) and
|
||||
exists(RValue outputstream, ClassInstanceExpr cie |
|
||||
cie = def.getSource() and
|
||||
outputstream = cie.getArgument(0) and
|
||||
adjacentUseUse(outputstream, sink)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A local variable that is assigned an `ObjectOutputStream`.
|
||||
* Writing tainted data to such a stream causes the underlying
|
||||
* `OutputStream` to be tainted.
|
||||
*/
|
||||
class ObjectOutputStreamVar extends LocalVariableDecl {
|
||||
ObjectOutputStreamVar() {
|
||||
exists(ClassInstanceExpr cie | cie = this.getAnAssignedValue() |
|
||||
cie.getType() instanceof TypeObjectOutputStream
|
||||
)
|
||||
}
|
||||
|
||||
VariableAssign getADef() {
|
||||
result.getSource().(ClassInstanceExpr).getType() instanceof TypeObjectOutputStream and
|
||||
result.getDestVar() = this
|
||||
}
|
||||
|
||||
MethodAccess getAWriteObjectMethodAccess() {
|
||||
result.getQualifier() = getAnAccess() and
|
||||
result.getMethod().hasName("writeObject")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A local variable that is initialized to a `StringBuilder`
|
||||
* or `StringBuffer`. Such variables are often used to
|
||||
* build up a query using string concatenation.
|
||||
*/
|
||||
class StringBuilderVar extends LocalVariableDecl {
|
||||
StringBuilderVar() {
|
||||
this.getType() instanceof TypeStringBuilder or
|
||||
this.getType() instanceof TypeStringBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that adds something to this string builder, from the argument at the given index.
|
||||
*/
|
||||
MethodAccess getAnInput(int arg) {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
(
|
||||
result.getMethod().getName() = "append" and arg = 0
|
||||
or
|
||||
result.getMethod().getName() = "insert" and arg = 1
|
||||
or
|
||||
result.getMethod().getName() = "replace" and arg = 2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that appends something to this string builder.
|
||||
*/
|
||||
MethodAccess getAnAppend() {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
result.getMethod().getName() = "append"
|
||||
}
|
||||
|
||||
MethodAccess getNextAppend(MethodAccess append) {
|
||||
result = getAnAppend() and
|
||||
append = getAnAppend() and
|
||||
(
|
||||
result.getQualifier() = append
|
||||
or
|
||||
not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and
|
||||
exists(RValue sbva1, RValue sbva2 |
|
||||
adjacentUseUse(sbva1, sbva2) and
|
||||
append.getQualifier() = getAChainedReference(sbva1) and
|
||||
result.getQualifier() = sbva2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that converts this string builder to a string.
|
||||
*/
|
||||
MethodAccess getToStringCall() {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
result.getMethod().getName() = "toString"
|
||||
}
|
||||
|
||||
private Expr getAChainedReference(VarAccess sbva) {
|
||||
// All the methods on `StringBuilder` that return the same type return
|
||||
// the same object.
|
||||
result = callReturningSameType+(sbva) and sbva = this.getAnAccess()
|
||||
or
|
||||
result = sbva and sbva = this.getAnAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that refers to this `StringBuilder`, possibly after some chained calls.
|
||||
*/
|
||||
Expr getAChainedReference() { result = getAChainedReference(_) }
|
||||
}
|
||||
|
||||
private MethodAccess callReturningSameType(Expr ref) {
|
||||
ref = result.getQualifier() and
|
||||
result.getMethod().getReturnType() = ref.getType()
|
||||
deprecated class Configuration2 = TaintTracking2::Configuration;
|
||||
}
|
||||
|
||||
7
java/ql/src/semmle/code/java/dataflow/TaintTracking2.qll
Normal file
7
java/ql/src/semmle/code/java/dataflow/TaintTracking2.qll
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import semmle.code.java.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
|
||||
|
||||
@@ -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, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -350,8 +350,18 @@ predicate hasNonlocalValue(FieldRead fr) {
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node node1, Node node2) {
|
||||
simpleLocalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 node1, Node node2) {
|
||||
// Variable flow steps through adjacent def-use and use-use pairs.
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
|
||||
@@ -0,0 +1,641 @@
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.DefUse
|
||||
private import semmle.code.java.security.SecurityTests
|
||||
private import semmle.code.java.security.Validation
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
private import semmle.code.java.Maps
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink`.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
DataFlow::localFlowStep(src, sink) or
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
|
||||
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(ArrayCreationExpr).getInit() = src
|
||||
or
|
||||
sink.(ArrayInit).getAnInit() = src
|
||||
or
|
||||
sink.(ArrayAccess).getArray() = src
|
||||
or
|
||||
sink.(LogicExpr).getAnOperand() = src
|
||||
or
|
||||
exists(Assignment assign | assign.getSource() = src |
|
||||
sink = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
or
|
||||
exists(EnhancedForStmt for, SsaExplicitUpdate v |
|
||||
for.getExpr() = src and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = sink
|
||||
)
|
||||
or
|
||||
containerStep(src, sink)
|
||||
or
|
||||
constructorStep(src, sink)
|
||||
or
|
||||
qualifierToMethodStep(src, sink)
|
||||
or
|
||||
qualifierToArgumentStep(src, sink)
|
||||
or
|
||||
argToMethodStep(src, sink)
|
||||
or
|
||||
argToArgStep(src, sink)
|
||||
or
|
||||
argToQualifierStep(src, sink)
|
||||
or
|
||||
comparisonStep(src, sink)
|
||||
or
|
||||
stringBuilderStep(src, sink)
|
||||
or
|
||||
serializationStep(src, sink)
|
||||
}
|
||||
|
||||
private class BulkData extends RefType {
|
||||
BulkData() {
|
||||
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
|
||||
or
|
||||
exists(RefType t | this.getASourceSupertype*() = t |
|
||||
t.hasQualifiedName("java.io", "InputStream") or
|
||||
t.hasQualifiedName("java.nio", "ByteBuffer") or
|
||||
t.hasQualifiedName("java.lang", "Readable") or
|
||||
t.hasQualifiedName("java.io", "DataInput") or
|
||||
t.hasQualifiedName("java.nio.channels", "ReadableByteChannel")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a constructor for a subclass of `java.io.InputStream` that
|
||||
* wraps an underlying data source. The underlying data source is given as a
|
||||
* the `argi`'th parameter to the constructor.
|
||||
*
|
||||
* An object construction of such a wrapper is likely to preserve the data flow
|
||||
* status of its argument.
|
||||
*/
|
||||
private predicate inputStreamWrapper(Constructor c, int argi) {
|
||||
c.getParameterType(argi) instanceof BulkData and
|
||||
c.getDeclaringType().getASourceSupertype().hasQualifiedName("java.io", "InputStream")
|
||||
}
|
||||
|
||||
/** An object construction that preserves the data flow status of any of its arguments. */
|
||||
private predicate constructorStep(Expr tracked, ConstructorCall sink) {
|
||||
exists(int argi | sink.getArgument(argi) = tracked |
|
||||
exists(string s | sink.getConstructedType().getQualifiedName() = s |
|
||||
// String constructor does nothing to data
|
||||
s = "java.lang.String" and argi = 0
|
||||
or
|
||||
// some readers preserve the content of streams
|
||||
s = "java.io.InputStreamReader" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedReader" and argi = 0
|
||||
or
|
||||
s = "java.io.CharArrayReader" and argi = 0
|
||||
or
|
||||
s = "java.io.StringReader" and argi = 0
|
||||
or
|
||||
// data preserved through streams
|
||||
s = "java.io.ObjectInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.ByteArrayInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.DataInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedInputStream" and argi = 0
|
||||
or
|
||||
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
|
||||
or
|
||||
s = "java.beans.XMLDecoder" and argi = 0
|
||||
or
|
||||
// a tokenizer preserves the content of a string
|
||||
s = "java.util.StringTokenizer" and argi = 0
|
||||
or
|
||||
// unzipping the stream preserves content
|
||||
s = "java.util.zip.ZipInputStream" and argi = 0
|
||||
or
|
||||
s = "java.util.zip.GZIPInputStream" and argi = 0
|
||||
or
|
||||
// string builders and buffers
|
||||
s = "java.lang.StringBuilder" and argi = 0
|
||||
or
|
||||
s = "java.lang.StringBuffer" and argi = 0
|
||||
or
|
||||
// a cookie with tainted ingredients is tainted
|
||||
s = "javax.servlet.http.Cookie" and argi = 0
|
||||
or
|
||||
s = "javax.servlet.http.Cookie" and argi = 1
|
||||
or
|
||||
// various xml stream source constructors.
|
||||
s = "org.xml.sax.InputSource" and argi = 0
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
|
||||
or
|
||||
s = "javax.xml.transform.stream.StreamSource" and argi = 0
|
||||
or
|
||||
//a URI constructed from a tainted string is tainted.
|
||||
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
|
||||
)
|
||||
or
|
||||
exists(RefType t | t.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(t, sink.getConstructedType())
|
||||
) and
|
||||
argi = 0
|
||||
or
|
||||
// wrappers constructed by extension
|
||||
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
|
||||
c = sink.getConstructor() and
|
||||
p = c.getParameter(argi) and
|
||||
sup.getEnclosingCallable() = c and
|
||||
constructorStep(p.getAnAccess(), sup)
|
||||
)
|
||||
or
|
||||
// a custom InputStream that wraps a tainted data source is tainted
|
||||
inputStreamWrapper(sink.getConstructor(), argi)
|
||||
)
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from qualifier to argument. */
|
||||
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma, int arg |
|
||||
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
|
||||
tracked = ma.getQualifier() and
|
||||
sink = ma.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
/** Methods that passes tainted data from qualifier to argument. */
|
||||
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
m.hasName("writeTo") and
|
||||
arg = 0
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
|
||||
m.hasName("read") and
|
||||
arg = 0
|
||||
or
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
|
||||
m.hasName("read") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from the qualifier. */
|
||||
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
|
||||
tracked = sink.getQualifier()
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods that return tainted data when called on tainted data.
|
||||
*/
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
m.getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
m.getName() = "concat" or
|
||||
m.getName() = "endsWith" or
|
||||
m.getName() = "getBytes" or
|
||||
m.getName() = "split" or
|
||||
m.getName() = "substring" or
|
||||
m.getName() = "toCharArray" or
|
||||
m.getName() = "toLowerCase" or
|
||||
m.getName() = "toString" or
|
||||
m.getName() = "toUpperCase" or
|
||||
m.getName() = "trim"
|
||||
)
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" | hasSubtype*(c, m.getDeclaringType())) and
|
||||
(
|
||||
m.getName().matches("to%String") or
|
||||
m.getName() = "toByteArray" or
|
||||
m.getName().matches("%Value")
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
|
||||
(
|
||||
m.getName() = "read" and m.getNumberOfParameters() = 0
|
||||
or
|
||||
m.getName() = "readLine"
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
|
||||
m.getName() = "toString"
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
|
||||
m.getName().matches("next%")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
(m.getName() = "toByteArray" or m.getName() = "toString")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and
|
||||
m.getName().matches("read%")
|
||||
or
|
||||
(
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
) and
|
||||
(m.getName() = "toString" or m.getName() = "append")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
|
||||
m.hasName("getInputSource")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
|
||||
m.hasName("getInputStream")
|
||||
or
|
||||
m instanceof IntentGetExtraMethod
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
|
||||
m.hasName("get")
|
||||
or
|
||||
m = any(GuiceProvider gp).getAnOverridingGetMethod()
|
||||
or
|
||||
m = any(ProtobufMessageLite p).getAGetterMethod()
|
||||
}
|
||||
|
||||
private class StringReplaceMethod extends Method {
|
||||
StringReplaceMethod() {
|
||||
getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
hasName("replace") or
|
||||
hasName("replaceAll") or
|
||||
hasName("replaceFirst")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unsafeEscape(MethodAccess ma) {
|
||||
// Removing `<script>` tags using a string-replace method is
|
||||
// unsafe if such a tag is embedded inside another one (e.g. `<scr<script>ipt>`).
|
||||
exists(StringReplaceMethod m | ma.getMethod() = m |
|
||||
ma.getArgument(0).(StringLiteral).getRepresentedString() = "(<script>)" and
|
||||
ma.getArgument(1).(StringLiteral).getRepresentedString() = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from an argument. */
|
||||
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
exists(Method m, int i |
|
||||
m = sink.(MethodAccess).getMethod() and
|
||||
taintPreservingArgumentToMethod(m, i) and
|
||||
tracked = sink.(MethodAccess).getArgument(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that return tainted data if its
|
||||
* `arg`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
method instanceof StringReplaceMethod and arg = 1
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(c, method.getDeclaringType())
|
||||
) and
|
||||
(
|
||||
method.getName().matches("parse%") and arg = 0
|
||||
or
|
||||
method.getName().matches("valueOf%") and arg = 0
|
||||
or
|
||||
method.getName().matches("to%String") and arg = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType() instanceof TypeString and
|
||||
method.getName() = "concat" and
|
||||
arg = 0
|
||||
or
|
||||
(
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
) and
|
||||
(
|
||||
method.getName() = "append" and arg = 0
|
||||
or
|
||||
method.getName() = "insert" and arg = 1
|
||||
or
|
||||
method.getName() = "replace" and arg = 2
|
||||
)
|
||||
or
|
||||
(
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
|
||||
) and
|
||||
(
|
||||
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "encodeToString" and arg = 0
|
||||
or
|
||||
method.getName() = "wrap" and arg = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.getName() = "buffer" and arg = 0
|
||||
or
|
||||
method.getName() = "readLines" and arg = 0
|
||||
or
|
||||
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.getName() = "toBufferedInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toBufferedReader" and arg = 0
|
||||
or
|
||||
method.getName() = "toByteArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toCharArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toString" and arg = 0
|
||||
)
|
||||
or
|
||||
// A URI created from a tainted string is still tainted.
|
||||
method.getDeclaringType().hasQualifiedName("java.net", "URI") and
|
||||
method.hasName("create") and
|
||||
arg = 0
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
|
||||
method.hasName("sourceToInputSource") and
|
||||
arg = 0
|
||||
or
|
||||
exists(ProtobufParser p | method = p.getAParseFromMethod()) and
|
||||
arg = 0
|
||||
or
|
||||
exists(ProtobufMessageLite m | method = m.getAParseFromMethod()) and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
|
||||
* between arguments.
|
||||
*/
|
||||
private predicate argToArgStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma, Method method, int input, int output |
|
||||
taintPreservingArgToArg(method, input, output) and
|
||||
ma.getMethod() = method and
|
||||
ma.getArgument(input) = tracked and
|
||||
ma.getArgument(output) = sink
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that writes tainted data to the
|
||||
* `output`th argument if the `input`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgToArg(Method method, int input, int output) {
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.hasName("copy") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("copyLarge") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("read") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("readFully") and
|
||||
input = 0 and
|
||||
output = 1 and
|
||||
not method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.hasName("write") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeChunked") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeLines") and input = 0 and output = 2
|
||||
or
|
||||
method.hasName("writeLines") and input = 1 and output = 2
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
|
||||
method.hasName("arraycopy") and
|
||||
input = 0 and
|
||||
output = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tracked` is the argument of a method that transfers taint
|
||||
* from the argument to the qualifier and `sink` is the qualifier.
|
||||
*/
|
||||
private predicate argToQualifierStep(Expr tracked, Expr sink) {
|
||||
exists(Method m, int i, MethodAccess ma |
|
||||
taintPreservingArgumentToQualifier(m, i) and
|
||||
ma.getMethod() = m and
|
||||
tracked = ma.getArgument(i) and
|
||||
sink = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a method that transfers taint from argument to qualifier and
|
||||
* `arg` is the index of the argument.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
|
||||
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
method.hasName("write") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/** A comparison or equality test with a constant. */
|
||||
private predicate comparisonStep(Expr tracked, Expr sink) {
|
||||
exists(Expr other |
|
||||
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof EqualityTest |
|
||||
e = sink and
|
||||
e.hasOperands(tracked, other)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess m | m.getMethod() instanceof EqualsMethod |
|
||||
m = sink and
|
||||
(
|
||||
m.getQualifier() = tracked and m.getArgument(0) = other
|
||||
or
|
||||
m.getQualifier() = other and m.getArgument(0) = tracked
|
||||
)
|
||||
)
|
||||
|
|
||||
other.isCompileTimeConstant() or other instanceof NullLiteral
|
||||
)
|
||||
}
|
||||
|
||||
/** Flow through a `StringBuilder`. */
|
||||
private predicate stringBuilderStep(Expr tracked, Expr sink) {
|
||||
exists(StringBuilderVar sbvar, MethodAccess input, int arg |
|
||||
input = sbvar.getAnInput(arg) and
|
||||
tracked = input.getArgument(arg) and
|
||||
sink = sbvar.getToStringCall()
|
||||
)
|
||||
}
|
||||
|
||||
/** Flow through data serialization. */
|
||||
private predicate serializationStep(Expr tracked, Expr sink) {
|
||||
exists(ObjectOutputStreamVar v, VariableAssign def |
|
||||
def = v.getADef() and
|
||||
exists(MethodAccess ma, RValue use |
|
||||
ma.getArgument(0) = tracked and
|
||||
ma = v.getAWriteObjectMethodAccess() and
|
||||
use = ma.getQualifier() and
|
||||
defUsePair(def, use)
|
||||
) and
|
||||
exists(RValue outputstream, ClassInstanceExpr cie |
|
||||
cie = def.getSource() and
|
||||
outputstream = cie.getArgument(0) and
|
||||
adjacentUseUse(outputstream, sink)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A local variable that is assigned an `ObjectOutputStream`.
|
||||
* Writing tainted data to such a stream causes the underlying
|
||||
* `OutputStream` to be tainted.
|
||||
*/
|
||||
class ObjectOutputStreamVar extends LocalVariableDecl {
|
||||
ObjectOutputStreamVar() {
|
||||
exists(ClassInstanceExpr cie | cie = this.getAnAssignedValue() |
|
||||
cie.getType() instanceof TypeObjectOutputStream
|
||||
)
|
||||
}
|
||||
|
||||
VariableAssign getADef() {
|
||||
result.getSource().(ClassInstanceExpr).getType() instanceof TypeObjectOutputStream and
|
||||
result.getDestVar() = this
|
||||
}
|
||||
|
||||
MethodAccess getAWriteObjectMethodAccess() {
|
||||
result.getQualifier() = getAnAccess() and
|
||||
result.getMethod().hasName("writeObject")
|
||||
}
|
||||
}
|
||||
private import StringBuilderVarModule
|
||||
|
||||
module StringBuilderVarModule {
|
||||
/**
|
||||
* A local variable that is initialized to a `StringBuilder`
|
||||
* or `StringBuffer`. Such variables are often used to
|
||||
* build up a query using string concatenation.
|
||||
*/
|
||||
class StringBuilderVar extends LocalVariableDecl {
|
||||
StringBuilderVar() {
|
||||
this.getType() instanceof TypeStringBuilder or
|
||||
this.getType() instanceof TypeStringBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that adds something to this string builder, from the argument at the given index.
|
||||
*/
|
||||
MethodAccess getAnInput(int arg) {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
(
|
||||
result.getMethod().getName() = "append" and arg = 0
|
||||
or
|
||||
result.getMethod().getName() = "insert" and arg = 1
|
||||
or
|
||||
result.getMethod().getName() = "replace" and arg = 2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that appends something to this string builder.
|
||||
*/
|
||||
MethodAccess getAnAppend() {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
result.getMethod().getName() = "append"
|
||||
}
|
||||
|
||||
MethodAccess getNextAppend(MethodAccess append) {
|
||||
result = getAnAppend() and
|
||||
append = getAnAppend() and
|
||||
(
|
||||
result.getQualifier() = append
|
||||
or
|
||||
not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and
|
||||
exists(RValue sbva1, RValue sbva2 |
|
||||
adjacentUseUse(sbva1, sbva2) and
|
||||
append.getQualifier() = getAChainedReference(sbva1) and
|
||||
result.getQualifier() = sbva2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that converts this string builder to a string.
|
||||
*/
|
||||
MethodAccess getToStringCall() {
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
result.getMethod().getName() = "toString"
|
||||
}
|
||||
|
||||
private Expr getAChainedReference(VarAccess sbva) {
|
||||
// All the methods on `StringBuilder` that return the same type return
|
||||
// the same object.
|
||||
result = callReturningSameType+(sbva) and sbva = this.getAnAccess()
|
||||
or
|
||||
result = sbva and sbva = this.getAnAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that refers to this `StringBuilder`, possibly after some chained calls.
|
||||
*/
|
||||
Expr getAChainedReference() { result = getAChainedReference(_) }
|
||||
}
|
||||
}
|
||||
|
||||
private MethodAccess callReturningSameType(Expr ref) {
|
||||
ref = result.getQualifier() and
|
||||
result.getMethod().getReturnType() = ref.getType()
|
||||
}
|
||||
@@ -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.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.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.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow2::DataFlow2 as DataFlow
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
* The set of dispatch targets for `Object.toString()` calls are reduced based
|
||||
* on possible data flow from objects of more specific types to the qualifier.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
|
||||
@@ -2,4 +2,5 @@ import java
|
||||
import semmle.code.xml.AndroidManifest
|
||||
|
||||
from AndroidActivityXmlElement e
|
||||
select e.getResolvedComponentName(), e.getAnIntentFilterElement().getAnActionElement().getActionName()
|
||||
select e.getResolvedComponentName(),
|
||||
e.getAnIntentFilterElement().getAnActionElement().getActionName()
|
||||
|
||||
@@ -5,9 +5,7 @@ import semmle.code.java.dataflow.TaintTracking
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "conf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src instanceof RemoteFlowSource
|
||||
}
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
|
||||
Reference in New Issue
Block a user