Deleted folder

This commit is contained in:
Andrei Diaconu
2019-07-23 09:40:58 +01:00
committed by AndreiDiaconu1
parent 45455a12d6
commit 50ba4d1fda
14 changed files with 0 additions and 8232 deletions

View File

@@ -1,23 +0,0 @@
/**
* Provides a library for local (intra-procedural) and global (inter-procedural)
* data flow analysis: deciding whether data can flow from a _source_ to a
* _sink_. This library differs from the one in `semmle.code.cpp.dataflow` in that
* this library uses the IR (Intermediate Representation) library, which provides
* a more precise semantic representation of the program, whereas the other dataflow
* library uses the more syntax-oriented ASTs. This library should provide more accurate
* results than the AST-based library in most scenarios.
*
* Unless configured otherwise, _flow_ means that the exact value of
* the source may reach the sink. We do not track flow across pointer
* dereferences or array indexing.
*
* To use global (interprocedural) data flow, extend the class
* `DataFlow::Configuration` as documented on that class. To use local
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
*/
import cpp
module DataFlow {
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
}

View File

@@ -1,38 +0,0 @@
/**
* Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow2 {
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
* Four copies are available: `DataFlow` through `DataFlow4`.
*/
private abstract
class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
super.hasFlow(source, sink)
}
}
}

View File

@@ -1,38 +0,0 @@
/**
* Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow3 {
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
* Four copies are available: `DataFlow` through `DataFlow4`.
*/
private abstract
class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
super.hasFlow(source, sink)
}
}
}

View File

@@ -1,38 +0,0 @@
/**
* Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow4 {
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
* Four copies are available: `DataFlow` through `DataFlow4`.
*/
private abstract
class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
super.hasFlow(source, sink)
}
}
}

View File

@@ -1,158 +0,0 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/
import semmle.code.cpp.ir.dataflow.DataFlow
import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.IR
module TaintTracking {
/**
* 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 `isAdditionalTaintStep`.
* }
* ```
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but it is unsupported to depend on a
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
* overridden predicates that define sources, sinks, or additional steps.
* Instead, the dependency should go to a `TaintTracking::Configuration2` or
* a `DataFlow{2,3,4}::Configuration`.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}
/**
* A taint-tracking configuration that is backed by the `DataFlow2` library
* instead of `DataFlow`. Use this class when taint-tracking configurations
* or data-flow configurations must depend on each other.
*
* See `TaintTracking::Configuration` for the full documentation.
*/
abstract class Configuration2 extends DataFlow2::Configuration {
bindingset[this]
Configuration2() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Taint can flow into using ordinary data flow.
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
nodeTo.getAnOperand().getDefinitionInstruction() = nodeFrom and
(
nodeTo instanceof ArithmeticInstruction
or
nodeTo instanceof BitwiseInstruction
or
nodeTo instanceof PointerArithmeticInstruction
or
nodeTo instanceof FieldAddressInstruction
)
or
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
}
/**
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
}

View File

@@ -1,80 +0,0 @@
private import semmle.code.cpp.ir.IR
Function viableImpl(CallInstruction call) { result = viableCallable(call) }
/**
* Gets a function that might be called by `call`.
*/
Function viableCallable(CallInstruction call) {
result = call.getStaticCallTarget()
or
// If the target of the call does not have a body in the snapshot, it might
// be because the target is just a header declaration, and the real target
// will be determined at run time when the caller and callee are linked
// together by the operating system's dynamic linker. In case a _unique_
// function with the right signature is present in the database, we return
// that as a potential callee.
exists(string qualifiedName, int nparams |
callSignatureWithoutBody(qualifiedName, nparams, call) and
functionSignatureWithBody(qualifiedName, nparams, result) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
}
/**
* Holds if `f` is a function with a body that has name `qualifiedName` and
* `nparams` parameter count. See `functionSignature`.
*/
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
functionSignature(f, qualifiedName, nparams) and
exists(f.getBlock())
}
/**
* Holds if the target of `call` is a function _with no definition_ that has
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
*/
pragma[noinline]
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) {
exists(Function target |
target = call.getStaticCallTarget() and
not exists(target.getBlock()) and
functionSignature(target, qualifiedName, nparams)
)
}
/**
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
* an approximation of its signature for the purpose of matching functions that
* might be the same across link targets.
*/
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
qualifiedName = f.getQualifiedName() and
nparams = f.getNumberOfParameters() and
not f.isStatic()
}
/**
* Holds if the call context `ctx` reduces the set of viable dispatch
* targets of `ma` in `c`.
*/
predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) { none() }
/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s for which the context makes a difference.
*/
Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
/**
* Holds if flow returning from `m` to `ma` might return further and if
* this path restricts the set of call sites that can be returned to.
*/
predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() }
/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s and results for which the return flow from the
* result to `ma` restricts the possible context `ctx`.
*/
Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() }

View File

@@ -1,469 +0,0 @@
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
cached
private module ImplCommon {
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
p.isParameterOf(viableCallable(call), i)
}
/**
* Holds if `arg` is a possible argument to `p` in `call`, taking virtual
* dispatch into account.
*/
cached
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i)
)
}
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account.
*/
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
p = node
or
exists(Node mid |
parameterValueFlowNoCtx(p, mid) and
localFlowStep(mid, node) and
compatibleTypes(p.getType(), node.getType())
)
or
// flow through a callable
exists(Node arg |
parameterValueFlowNoCtx(p, arg) and
argumentValueFlowsThroughNoCtx(arg, node) and
compatibleTypes(p.getType(), node.getType())
)
}
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps, not taking call contexts
* into account.
*/
private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
}
pragma[nomagic]
private predicate argumentValueFlowsThroughNoCtx0(
DataFlowCall call, ArgumentNode arg, ReturnKind kind
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
parameterValueFlowsThroughNoCtx(param, kind)
)
}
/**
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
* not taking call contexts into account.
*/
private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
out = getAnOutNode(call, kind) and
compatibleTypes(arg.getType(), out.getType())
)
}
/**
* Holds if `arg` is the `i`th argument of `call` inside the callable
* `enclosing`, and `arg` may flow through `call`.
*/
pragma[noinline]
private predicate argumentOf(
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
) {
arg.argumentOf(call, i) and
argumentValueFlowsThroughNoCtx(arg, _) and
enclosing = arg.getEnclosingCallable()
}
pragma[noinline]
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
pragma[noinline]
private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
outercc = TAnyCallContext()
or
outercc = TSomeCall(getAParameter(c), _)
or
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
reducedViableImplInCallContext(_, c, other)
)
)
}
pragma[noinline]
private predicate viableParamArg1(
ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
DataFlowCall call
) {
viableParamArg0(i, arg, outercc, call) and
callable = resolveCall(call, outercc) and
p.isParameterOf(callable, any(int j | j <= i and j >= i))
}
/**
* Holds if `arg` is a possible argument to `p`, in the call `call`, and
* `arg` may flow through `call`. The possible contexts before and after
* entering the callable are `outercc` and `innercc`, respectively.
*/
private predicate viableParamArg(
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
CallContextCall innercc
) {
exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
if reducedViableImplInCallContext(_, callable, call)
then innercc = TSpecificCall(call, i, true)
else innercc = TSomeCall(p, true)
)
}
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
result = TSomeCall(p, _)
or
exists(DataFlowCall call, int i, DataFlowCallable callable |
result = TSpecificCall(call, i, _) and
p.isParameterOf(callable, i) and
reducedViableImplInCallContext(_, callable, call)
)
}
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, in call context `cc`.
*/
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
p = node and
parameterValueFlowsThroughNoCtx(p, _) and
cc = getAValidCallContextForParameter(p)
or
exists(Node mid |
parameterValueFlow(p, mid, cc) and
localFlowStep(mid, node) and
compatibleTypes(p.getType(), node.getType())
)
or
// flow through a callable
exists(Node arg |
parameterValueFlow(p, arg, cc) and
argumentValueFlowsThrough(arg, node, cc) and
compatibleTypes(p.getType(), node.getType())
)
}
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps, in call context `cc`.
*/
cached
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
) {
exists(ParameterNode param, CallContext innercc |
viableParamArg(call, param, arg, cc, innercc) and
parameterValueFlowsThrough(param, kind, innercc)
)
}
/**
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
* in call context cc.
*/
cached
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
out = getAnOutNode(call, kind) and
compatibleTypes(arg.getType(), out.getType())
)
}
/**
* Holds if `p` can flow to the pre-update node of `n` in the same callable
* using only value-preserving steps.
*/
cached
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
parameterValueFlowNoCtx(p, n.getPreUpdateNode())
}
/**
* Holds if data can flow from `node1` to `node2` in one local step or a step
* through a value-preserving method.
*/
private predicate localValueStep(Node node1, Node node2) {
localFlowStep(node1, node2) or
argumentValueFlowsThrough(node1, node2, _)
}
/*
* Calculation of `predicate store(Node node1, Content f, Node node2)`:
* There are three cases:
* - The base case: A direct local assignment given by `storeStep`.
* - A call to a method or constructor with two arguments, `arg1` and `arg2`,
* such the call has the side-effect `arg2.f = arg1`.
* - A call to a method that returns an object in which an argument has been
* stored.
* `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
* the third case.
*/
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f` or via a call that acts as a setter.
*/
cached
predicate store(Node node1, Content f, Node node2) {
storeViaSideEffect(node1, f, node2) or
storeReturn(node1, f, node2)
}
private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
storeStep(node1, f, node2) and readStep(_, f, _)
or
exists(DataFlowCall call, int i1, int i2 |
setterCall(call, i1, i2, f) and
node1.(ArgumentNode).argumentOf(call, i1) and
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
compatibleTypes(node1.getTypeBound(), f.getType()) and
compatibleTypes(node2.getTypeBound(), f.getContainerType())
)
}
pragma[nomagic]
private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
exists(Node n1, PostUpdateNode n2 |
parameterValueFlowNoCtx(p1, n1) and
storeViaSideEffect(n1, f, n2) and
parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
p1 != p2
)
}
pragma[nomagic]
private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
setterInParam(p1, f, p2) and
callable = viableCallable(call) and
p1.isParameterOf(callable, i1) and
p2.isParameterOf(callable, i2)
)
}
pragma[noinline]
private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
exists(ParameterNode p |
viableParamArg(call, p, arg) and
setterReturn(p, f, kind)
)
}
private predicate storeReturn(Node node1, Content f, Node node2) {
exists(DataFlowCall call, ReturnKind kind |
storeReturn0(call, kind, node1, f) and
node2 = getAnOutNode(call, kind) and
compatibleTypes(node1.getTypeBound(), f.getType()) and
compatibleTypes(node2.getTypeBound(), f.getContainerType())
)
}
private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
exists(Node n1, Node n2 |
parameterValueFlowNoCtx(p, n1) and
store(n1, f, n2) and
localValueStep*(n2, getAReturnNodeOfKind(kind))
)
}
pragma[noinline]
private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
exists(ParameterNode p |
viableParamArg(call, p, arg) and
getter(p, f, kind)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct read of `f` or
* via a getter.
*/
cached
predicate read(Node node1, Content f, Node node2) {
readStep(node1, f, node2) and storeStep(_, f, _)
or
exists(DataFlowCall call, ReturnKind kind |
read0(call, kind, node1, f) and
node2 = getAnOutNode(call, kind) and
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
compatibleTypes(node2.getTypeBound(), f.getType())
)
}
private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
exists(Node n1, Node n2 |
parameterValueFlowNoCtx(p, n1) and
read(n1, f, n2) and
localValueStep*(n2, getAReturnNodeOfKind(kind))
)
}
cached
predicate localStoreReadStep(Node node1, Node node2) {
exists(Node mid1, Node mid2, Content f |
store(node1, f, mid1) and
localValueStep*(mid1, mid2) and
read(mid2, f, node2)
)
}
/**
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
* expression that reaches a `this` parameter.
*/
private predicate callHasInstanceArgument(DataFlowCall call) {
exists(ArgumentNode arg | arg.argumentOf(call, -1))
}
cached
newtype TCallContext =
TAnyCallContext() or
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
reducedViableImplInCallContext(_, _, call) and
(emptyAp = true or emptyAp = false) and
(
exists(call.getArgument(i))
or
i = -1 and callHasInstanceArgument(call)
)
} or
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
cached
newtype TReturnPosition =
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
}
import ImplCommon
pragma[noinline]
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
c = returnNodeGetEnclosingCallable(ret) and
kind = ret.getKind()
}
/**
* A call context to restrict the targets of virtual dispatch and match the
* call sites of flow into a method with flow out of a method.
*
* There are four cases:
* - `TAnyCallContext()` : No restrictions on method flow.
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
* parameter at the given `call`. This call improves the set of viable
* dispatch targets for at least one method call in the current callable.
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
* originating call does not improve the set of dispatch targets for any
* method call in the current callable and was therefore not recorded.
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
* this dispatch target of `call` implies a reduced set of dispatch origins
* to which data may flow if it should reach a `return` statement.
*/
abstract class CallContext extends TCallContext {
abstract string toString();
}
class CallContextAny extends CallContext, TAnyCallContext {
override string toString() { result = "CcAny" }
}
abstract class CallContextCall extends CallContext { }
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
result = "CcCall(" + call + ", " + i + ")"
)
}
}
class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
}
class CallContextReturn extends CallContext, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}
}
/** A callable tagged with a relevant return kind. */
class ReturnPosition extends TReturnPosition0 {
private DataFlowCallable c;
private ReturnKind kind;
ReturnPosition() { this = TReturnPosition0(c, kind) }
/** Gets the callable. */
DataFlowCallable getCallable() { result = c }
/** Gets the return kind. */
ReturnKind getKind() { result = kind }
/** Gets a textual representation of this return position. */
string toString() { result = "[" + kind + "] " + c }
}
pragma[noinline]
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
result = ret.getEnclosingCallable()
}
pragma[noinline]
ReturnPosition getReturnPosition(ReturnNode ret) {
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
result = TReturnPosition0(c, k)
)
}
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
cc instanceof CallContextAny and callable = viableCallable(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
cc = TReturn(c0, call0) and
c0 = prunedViableImplInCallContextReverse(call0, call)
)
}
bindingset[call, cc]
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
else result = viableCallable(call)
)
or
result = viableCallable(call) and cc instanceof CallContextSomeCall
or
result = viableCallable(call) and cc instanceof CallContextAny
or
result = viableCallable(call) and cc instanceof CallContextReturn
}

View File

@@ -1,11 +0,0 @@
/**
* Provides IR-specific definitions for use in the data flow library.
*/
module Private {
import DataFlowPrivate
import DataFlowDispatch
}
module Public {
import DataFlowUtil
}

View File

@@ -1,202 +0,0 @@
private import cpp
private import DataFlowUtil
private import semmle.code.cpp.ir.IR
private import DataFlowDispatch
/**
* A data flow node that occurs as the argument of a call and is passed as-is
* to the callable. Instance arguments (`this` pointer) are also included.
*/
class ArgumentNode extends Node {
ArgumentNode() { exists(CallInstruction call | this = call.getAnArgument()) }
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
predicate argumentOf(DataFlowCall call, int pos) {
this = call.getPositionalArgument(pos)
or
this = call.getThisArgument() and pos = -1
}
/** Gets the call in which this node is an argument. */
DataFlowCall getCall() { this.argumentOf(result, _) }
}
private newtype TReturnKind = TNormalReturnKind()
/**
* A return kind. A return kind describes how a value can be returned
* from a callable. For C++, this is simply a function return.
*/
class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */
string toString() { result = "return" }
}
/** A data flow node that occurs as the result of a `ReturnStmt`. */
class ReturnNode extends Node {
ReturnNode() { exists(ReturnValueInstruction ret | this = ret.getReturnValue()) }
/** Gets the kind of this returned value. */
ReturnKind getKind() { result = TNormalReturnKind() }
}
/** A data flow node that represents the output of a call. */
class OutNode extends Node, CallInstruction {
/** Gets the underlying call. */
DataFlowCall getCall() { result = this }
}
/**
* Gets a node that can read the value returned from `call` with return kind
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result = call and
kind = TNormalReturnKind()
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
predicate jumpStep(Node n1, Node n2) { none() }
/**
* Holds if `call` passes an implicit or explicit qualifier, i.e., a
* `this` parameter.
*/
predicate callHasQualifier(Call call) {
call.hasQualifier()
or
call.getTarget() instanceof Destructor
}
private newtype TContent =
TFieldContent(Field f) or
TCollectionContent() or
TArrayContent()
/**
* A reference contained in an object. Examples include instance fields, the
* contents of a collection object, or the contents of an array.
*/
class Content extends TContent {
/** Gets a textual representation of this element. */
abstract string toString();
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
/** Gets the type of the object containing this content. */
abstract Type getContainerType();
/** Gets the type of this content. */
abstract Type getType();
}
private class FieldContent extends Content, TFieldContent {
Field f;
FieldContent() { this = TFieldContent(f) }
Field getField() { result = f }
override string toString() { result = f.toString() }
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
override Type getContainerType() { result = f.getDeclaringType() }
override Type getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
override Type getContainerType() { none() }
override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
override Type getContainerType() { none() }
override Type getType() { none() }
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
none() // stub implementation
}
/**
* Holds if data can flow from `node1` to `node2` via a read of `f`.
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
predicate readStep(Node node1, Content f, Node node2) {
none() // stub implementation
}
/**
* Gets a representative (boxed) type for `t` for the purpose of pruning
* possible flow. A single type is used for all numeric types to account for
* numeric conversions, and otherwise the erasure is used.
*/
Type getErasedRepr(Type t) {
suppressUnusedType(t) and
result instanceof VoidType // stub implementation
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(Type t) { result = t.toString() }
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
predicate compatibleTypes(Type t1, Type t2) {
any() // stub implementation
}
private predicate suppressUnusedType(Type t) { any() }
//////////////////////////////////////////////////////////////////////////////
// Java QL library compatibility wrappers
//////////////////////////////////////////////////////////////////////////////
/** A node that performs a type cast. */
class CastNode extends Node {
CastNode() { none() } // stub implementation
}
class DataFlowCallable = Function;
class DataFlowExpr = Expr;
class DataFlowType = Type;
class DataFlowLocation = Location;
/** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction, Node {
/**
* Gets the nth argument for this call.
*
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
*/
Node getArgument(int n) { result = this.getPositionalArgument(n) }
}

View File

@@ -1,155 +0,0 @@
/**
* Provides C++-specific definitions for use in the data flow library.
*/
private import cpp
private import semmle.code.cpp.ir.IR
/**
* A node in a data flow graph.
*
* A node can be either an expression, a parameter, or an uninitialized local
* variable. Such nodes are created with `DataFlow::exprNode`,
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
*/
class Node extends Instruction {
/**
* INTERNAL: Do not use. Alternative name for `getFunction`.
*/
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
/** Gets the type of this node. */
Type getType() { result = this.getResultType() }
/**
* Gets the non-conversion expression corresponding to this node, if any. If
* this node strictly (in the sense of `asConvertedExpr`) corresponds to a
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
* expression.
*/
Expr asExpr() {
result.getConversion*() = this.getConvertedResultExpression() and
not result instanceof Conversion
}
/**
* Gets the expression corresponding to this node, if any. The returned
* expression may be a `Conversion`.
*/
Expr asConvertedExpr() { result = this.getConvertedResultExpression() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.(InitializeParameterInstruction).getParameter() }
/**
* Gets the uninitialized local variable corresponding to this node, if
* any.
*/
LocalVariable asUninitialized() { result = this.(UninitializedInstruction).getLocalVariable() }
/**
* Gets an upper bound on the type of this node.
*/
Type getTypeBound() { result = getType() }
}
/**
* An expression, viewed as a node in a data flow graph.
*/
class ExprNode extends Node {
ExprNode() { exists(this.asExpr()) }
/**
* Gets the non-conversion expression corresponding to this node, if any. If
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
* expression.
*/
Expr getExpr() { result = this.asExpr() }
/**
* Gets the expression corresponding to this node, if any. The returned
* expression may be a `Conversion`.
*/
Expr getConvertedExpr() { result = this.asConvertedExpr() }
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node, InitializeParameterInstruction {
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)
* position. The implicit `this` parameter is considered to have index `-1`.
*/
predicate isParameterOf(Function f, int i) { f.getParameter(i) = getParameter() }
}
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
*/
class UninitializedNode extends Node, UninitializedInstruction { }
/**
* A node associated with an object after an operation that might have
* changed its state.
*
* This can be either the argument to a callable after the callable returns
* (which might have mutated the argument), or the qualifier of a field after
* an update to the field.
*
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
* to the value before the update with the exception of `ClassInstanceExpr`,
* which represents the value after the constructor has run.
*
* This class exists to match the interface used by Java. There are currently no non-abstract
* classes that extend it. When we implement field flow, we can revisit this.
*/
abstract class PostUpdateNode extends Node {
/**
* Gets the node before the state update.
*/
abstract Node getPreUpdateNode();
}
/**
* Gets a `Node` corresponding to `e` or any of its conversions. There is no
* result if `e` is a `Conversion`.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
/**
* Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
* `Conversion`.
*/
ExprNode convertedExprNode(Expr e) { result.getExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
*/
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
/**
* Gets the `Node` corresponding to the value of an uninitialized local
* variable `v`.
*/
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
nodeTo.(CopyInstruction).getSourceValue() = nodeFrom or
nodeTo.(PhiInstruction).getAnOperand().getDefinitionInstruction() = nodeFrom or
// Treat all conversions as flow, even conversions between different numeric types.
nodeTo.(ConvertInstruction).getUnary() = nodeFrom
}
/**
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }