mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge rc/1.19 into next.
This commit is contained in:
@@ -9,11 +9,21 @@
|
||||
|
||||
import cpp
|
||||
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
module ASTDataFlow {
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
}
|
||||
|
||||
module IRDataFlow {
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow4
|
||||
}
|
||||
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
from File f, string tag
|
||||
|
||||
23
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll
Normal file
23
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow3.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow3.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow4.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow4.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
private import cpp
|
||||
private import DataFlowPrivate
|
||||
|
||||
Function viableImpl(MethodAccess ma) {
|
||||
result = ma.getTarget()
|
||||
}
|
||||
|
||||
Function viableCallable(Call call) {
|
||||
result = call.getTarget()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable dispatch
|
||||
* targets of `ma` in `c`.
|
||||
*/
|
||||
predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which a context might make a difference.
|
||||
*/
|
||||
private Method viableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
// stub implementation
|
||||
result = viableImpl(ma) and
|
||||
viableCallable(ctx) = ma.getEnclosingFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInCallContext(ma, _, ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data might flow from `ma` to a return statement in some
|
||||
* configuration.
|
||||
*/
|
||||
private predicate maybeChainedReturn(MethodAccess ma) {
|
||||
exists(ReturnStmt ret |
|
||||
exists(ret.getExpr()) and
|
||||
ret.getEnclosingFunction() = ma.getEnclosingFunction() and
|
||||
not ma.getParent() instanceof ExprStmt
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Method m, MethodAccess ma) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
m = viableImpl(ma) and
|
||||
ctxtgts = count(Call ctx | m = viableImplInCallContext(ma, ctx)) and
|
||||
tgts = strictcount(Call ctx | viableCallable(ctx) = ma.getEnclosingFunction()) and
|
||||
ctxtgts < tgts
|
||||
) and
|
||||
maybeChainedReturn(ma)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInReturn(result, ma)
|
||||
}
|
||||
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
|
||||
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(Call call, int i, ParameterNode p) {
|
||||
exists(Callable callable |
|
||||
callable = viableCallable(call) and
|
||||
p.isParameterOf(callable, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i, Call call |
|
||||
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.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node) {
|
||||
p = node
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a `ReturnNode` in the same callable using only
|
||||
* value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsThrough(ParameterNode p) {
|
||||
exists(ReturnNode ret | parameterValueFlow(p, ret))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows through `call` using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ExprNode call) {
|
||||
exists(ParameterNode param |
|
||||
viableParamArg(param, arg) and
|
||||
parameterValueFlowsThrough(param) and
|
||||
arg.argumentOf(call.getExpr(), _) and
|
||||
compatibleTypes(arg.getType(), call.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) {
|
||||
parameterValueFlow(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(Call 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 |
|
||||
parameterValueFlow(p1, n1) and
|
||||
storeViaSideEffect(n1, f, n2) and
|
||||
parameterValueFlow(p2, n2.getPreUpdateNode()) and
|
||||
p1 != p2
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate setterCall(Call call, int i1, int i2, Content f) {
|
||||
exists(Callable callable, ParameterNode p1, ParameterNode p2 |
|
||||
setterInParam(p1, f, p2) and
|
||||
callable = viableCallable(call) and
|
||||
p1.isParameterOf(callable, i1) and
|
||||
p2.isParameterOf(callable, i2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate storeReturn(Node node1, Content f, Node node2) {
|
||||
exists(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
)
|
||||
}
|
||||
|
||||
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(Call call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(Call 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(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) }
|
||||
}
|
||||
import ImplCommon
|
||||
|
||||
/**
|
||||
* 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(Call 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(Method m, MethodAccess ma)` : Flow reached `ma` from `m` and
|
||||
* this dispatch target of `ma` 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() { result = "CcCall" }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() { result = "CcReturn" }
|
||||
}
|
||||
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, Callable callable, Call call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
or
|
||||
exists(Method m0, MethodAccess ma0 |
|
||||
ma0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(m0, ma0) and
|
||||
m0 = prunedViableImplInCallContextReverse(ma0, call)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[call, cc]
|
||||
Callable resolveCall(Call call, CallContext cc) {
|
||||
exists(Call 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
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
private import cpp
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* 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(Call call, int pos) {
|
||||
exists (CallInstruction callInstr |
|
||||
callInstr.getAST() = call and
|
||||
(
|
||||
this = callInstr.getPositionalArgument(pos) or
|
||||
this = callInstr.getThisArgument() and pos = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node {
|
||||
ReturnNode() {
|
||||
exists(ReturnValueInstruction ret | this = ret.getReturnValue() )
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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` does not pass 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 RefType 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 RefType getContainerType() { result = f.getDeclaringType() }
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getContainerType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType 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.
|
||||
*/
|
||||
RefType 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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RefType extends Type {
|
||||
}
|
||||
|
||||
class CastExpr extends Expr {
|
||||
CastExpr() { none() } // stub implementation
|
||||
}
|
||||
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
Argument() {
|
||||
call.getArgument(pos) = this
|
||||
}
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() {
|
||||
result = pos
|
||||
}
|
||||
}
|
||||
|
||||
class Callable extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `Function` in the C++ library. In the Java library, a `Method`
|
||||
* is any callable except a constructor.
|
||||
*/
|
||||
class Method extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `FunctionCall` in the C++ library. In the Java library, a
|
||||
* `MethodAccess` is any `Call` that does not call a constructor.
|
||||
*/
|
||||
class MethodAccess extends FunctionCall {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getEnclosingFunction`.
|
||||
*/
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.getEnclosingFunction()
|
||||
}
|
||||
}
|
||||
135
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
Normal file
135
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
import cpp
|
||||
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.getFunction()
|
||||
}
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.getResultType()
|
||||
}
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { 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 {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { expr = this.asExpr() }
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the `Node` corresponding to `e`.
|
||||
*/
|
||||
ExprNode exprNode(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).getOperand() = 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)
|
||||
}
|
||||
@@ -68,7 +68,8 @@ private newtype TOpcode =
|
||||
TBufferReadSideEffect() or
|
||||
TBufferWriteSideEffect() or
|
||||
TBufferMayWriteSideEffect() or
|
||||
TChi()
|
||||
TChi() or
|
||||
TUnreached()
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() {
|
||||
@@ -195,5 +196,6 @@ module Opcode {
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
|
||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
|
||||
class Chi extends Opcode, TChi {override final string toString() { result = "Chi" } }
|
||||
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
|
||||
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import Cached
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
|
||||
@@ -102,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -603,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1106,9 +1113,39 @@ class CallInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1432,6 +1469,17 @@ class ChiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,13 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
|
||||
@@ -19,6 +19,10 @@ private VirtualIRVariable getVirtualVariable(IRVariable var) {
|
||||
result.getIRVariable() = var
|
||||
}
|
||||
|
||||
private UnknownVirtualVariable getUnknownVirtualVariable(FunctionIR f) {
|
||||
result.getFunctionIR() = f
|
||||
}
|
||||
|
||||
class VirtualVariable extends TVirtualVariable {
|
||||
string toString() {
|
||||
none()
|
||||
@@ -83,10 +87,10 @@ class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable {
|
||||
}
|
||||
|
||||
private newtype TMemoryAccess =
|
||||
TVariableMemoryAccess(VirtualIRVariable vvar, IntValue offset, IntValue size) {
|
||||
TVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) {
|
||||
exists(Instruction instr |
|
||||
exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), vvar.getIRVariable(), offset) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, offset) and
|
||||
if exists(instr.getResultSize())
|
||||
then instr.getResultSize() = size
|
||||
else size = Ints::unknown()
|
||||
@@ -97,7 +101,7 @@ private newtype TMemoryAccess =
|
||||
TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv)
|
||||
|
||||
private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) {
|
||||
result.getVirtualVariable() = getVirtualVariable(var) and
|
||||
result.getVariable() = var and
|
||||
result.getOffset() = offset and
|
||||
result.getSize() = size
|
||||
}
|
||||
@@ -117,20 +121,21 @@ class MemoryAccess extends TMemoryAccess {
|
||||
}
|
||||
|
||||
class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
|
||||
VirtualIRVariable vvar;
|
||||
IRVariable var;
|
||||
IntValue offset;
|
||||
IntValue size;
|
||||
|
||||
VariableMemoryAccess() {
|
||||
this = TVariableMemoryAccess(vvar, offset, size)
|
||||
this = TVariableMemoryAccess(var, offset, size)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = vvar.toString()
|
||||
result = var.toString() + "[" + offset.toString() + ".." + (offset + size - 1).toString() + "]"
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
result = getVirtualVariable(var) or
|
||||
not exists(getVirtualVariable(var)) and result = getUnknownVirtualVariable(var.getFunctionIR())
|
||||
}
|
||||
|
||||
IntValue getOffset() {
|
||||
@@ -141,10 +146,15 @@ class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
|
||||
result = size
|
||||
}
|
||||
|
||||
final IRVariable getVariable() {
|
||||
result = var
|
||||
}
|
||||
|
||||
final override predicate isPartialMemoryAccess() {
|
||||
not exists(getVirtualVariable(var)) or
|
||||
getOffset() != 0
|
||||
or
|
||||
getSize() != vvar.getType().getSize()
|
||||
getSize() != var.getType().getSize()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +176,7 @@ class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess {
|
||||
final override predicate isPartialMemoryAccess() {
|
||||
any()
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
@@ -257,9 +268,10 @@ MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
if exists(IRVariable var, IntValue i |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i)
|
||||
)
|
||||
then exists(IRVariable var, IntValue i |
|
||||
then exists(IRVariable var, IntValue i, int size |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and
|
||||
result = getVariableMemoryAccess(var, i, operand.getDefinitionInstruction().getResultSize())
|
||||
result = getVariableMemoryAccess(var, i, size) and
|
||||
size = operand.getDefinitionInstruction().getResultSize()
|
||||
)
|
||||
else (
|
||||
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR())) and
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import AliasedSSA
|
||||
|
||||
/**
|
||||
* Property provide that dumps the memory access of each result. Useful for debugging SSA
|
||||
* construction.
|
||||
*/
|
||||
class PropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
key = "ResultMemoryAccess" and result = getResultMemoryAccess(instruction).toString()
|
||||
}
|
||||
}
|
||||
@@ -4,23 +4,35 @@ private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
WrappedInstructionTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
} or
|
||||
ChiTag(OldIR::Instruction oldInstruction) {
|
||||
ChiTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
|
||||
// We need an `Unreached` instruction for the destination of any edge whose predecessor
|
||||
// instruction is reachable, but whose successor block is not. This should occur only for
|
||||
// infeasible edges.
|
||||
exists(OldIR::Instruction succInstruction |
|
||||
succInstruction = oldInstruction.getSuccessor(kind) and
|
||||
not succInstruction instanceof OldInstruction
|
||||
)
|
||||
}
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
@@ -35,11 +47,11 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
cached OldInstruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
|
||||
private Instruction getNewInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewInstruction(OldInstruction instr) {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
@@ -47,21 +59,21 @@ cached private module Cached {
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewFinalInstruction(OldInstruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
|
||||
private ChiInstruction getChiInstruction (OldInstruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
@@ -91,8 +103,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldIR::Instruction instr |
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldInstruction instr |
|
||||
instr.getFunction() = func and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getAST() = ast and
|
||||
@@ -103,7 +115,7 @@ cached private module Cached {
|
||||
else
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
|
||||
exists(OldBlock block, Alias::VirtualVariable vvar |
|
||||
hasPhiNode(vvar, block) and
|
||||
block.getFunction() = func and
|
||||
opcode instanceof Opcode::Phi and
|
||||
@@ -112,7 +124,7 @@ cached private module Cached {
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
|
||||
exists(OldInstruction instr, Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, instr) and
|
||||
instr.getFunction() = func and
|
||||
opcode instanceof Opcode::Chi and
|
||||
@@ -120,11 +132,19 @@ cached private module Cached {
|
||||
tag = ChiTag(instr) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction oldInstruction, EdgeKind kind |
|
||||
oldInstruction.getFunction() = func and
|
||||
tag = UnreachedTag(oldInstruction, kind) and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
ast = oldInstruction.getSuccessor(kind).getAST() and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
|
||||
Type type) {
|
||||
Type type) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getFunction() = func and
|
||||
var.getAST() = ast and
|
||||
@@ -135,19 +155,20 @@ cached private module Cached {
|
||||
|
||||
cached predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
@@ -164,7 +185,7 @@ cached private module Cached {
|
||||
) or
|
||||
// Connect any definitions that are not being modeled in SSA to the
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
exists(OldInstruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
@@ -189,8 +210,8 @@ cached private module Cached {
|
||||
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
|
||||
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
@@ -205,8 +226,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
|
||||
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
|
||||
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
|
||||
int defRank, int defIndex, OldBlock useBlock, int useRank |
|
||||
ChiTag(oldInstr) = chiInstr.getTag() and
|
||||
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
@@ -220,7 +241,7 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldIR::IRBlock oldBlock |
|
||||
exists(OldBlock oldBlock |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
@@ -247,10 +268,11 @@ cached private module Cached {
|
||||
else (
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
) or
|
||||
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -310,29 +332,29 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
|
||||
OldInstruction instr, OldBlock block, int index) {
|
||||
block.getInstruction(index) = instr and
|
||||
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
|
||||
}
|
||||
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
|
||||
(
|
||||
hasPhiNode(vvar, block) and
|
||||
index = -1
|
||||
) or
|
||||
exists(Alias::MemoryAccess access, OldIR::Instruction def |
|
||||
exists(Alias::MemoryAccess access, OldInstruction def |
|
||||
access = Alias::getResultMemoryAccess(def) and
|
||||
block.getInstruction(index) = def and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
|
||||
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
|
||||
}
|
||||
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
|
||||
int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
(
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand())
|
||||
@@ -349,7 +371,7 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
exists (int index | hasUse(vvar, _, block, index) |
|
||||
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
|
||||
) or
|
||||
@@ -357,7 +379,7 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
|
||||
}
|
||||
|
||||
@@ -367,18 +389,18 @@ cached private module Cached {
|
||||
* end of the block, even if the definition is the last instruction in the
|
||||
* block.
|
||||
*/
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
|
||||
}
|
||||
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
int instructionIndex) {
|
||||
hasDefinition(vvar, block, instructionIndex) and
|
||||
defUseRank(vvar, block, rankIndex, instructionIndex)
|
||||
}
|
||||
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
|
||||
int rankIndex, OldIR::Instruction use) {
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
OldInstruction use) {
|
||||
exists(int index |
|
||||
hasUse(vvar, use, block, index) and
|
||||
defUseRank(vvar, block, rankIndex, index)
|
||||
@@ -389,8 +411,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
|
||||
* index `reachesRank` in block `block`.
|
||||
*/
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int defRank, int reachesRank) {
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
|
||||
int reachesRank) {
|
||||
hasDefinitionAtRank(vvar, block, defRank, _) and
|
||||
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
@@ -410,8 +432,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
|
||||
* block `block`.
|
||||
*/
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock block) {
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
(
|
||||
(
|
||||
@@ -421,7 +443,7 @@ cached private module Cached {
|
||||
variableLiveOnExitFromBlock(vvar, defBlock) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
|
||||
) or
|
||||
exists(OldIR::IRBlock idom |
|
||||
exists(OldBlock idom |
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
|
||||
noDefinitionsSinceIDominator(vvar, idom, block)
|
||||
)
|
||||
@@ -429,24 +451,23 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock idom, OldIR::IRBlock block) {
|
||||
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
|
||||
OldBlock block) {
|
||||
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
variableLiveOnExitFromBlock(vvar, block) and
|
||||
not hasDefinition(vvar, block, _)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUseWithinBlock(
|
||||
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
|
||||
OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
defBlock = useBlock and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, useRank)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
(
|
||||
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
|
||||
@@ -459,24 +480,21 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
exists(OldIR::IRBlock defBlock |
|
||||
phiBlock = defBlock.dominanceFrontier() and
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
hasDefinition(vvar, defBlock, _) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
variableLiveOnEntryToBlock(vvar, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction def) {
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
@@ -492,13 +510,17 @@ cached private module CachedForDebugging {
|
||||
}
|
||||
|
||||
cached string getInstructionUniqueId(Instruction instr) {
|
||||
exists(OldIR::Instruction oldInstr |
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
result = "NonSSA: " + oldInstr.getUniqueId()
|
||||
) or
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
|
||||
) or
|
||||
exists(OldInstruction oldInstr, EdgeKind kind |
|
||||
instr.getTag() = UnreachedTag(oldInstr, kind) and
|
||||
result = "Unreached(" + oldInstr.getUniqueId() + ":" + kind.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import Cached
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
|
||||
@@ -102,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -603,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1106,9 +1113,39 @@ class CallInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1432,6 +1469,17 @@ class ChiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,13 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -37,9 +38,18 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType and
|
||||
(
|
||||
if hasWriteSideEffect() then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType
|
||||
)
|
||||
else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType instanceof VoidType
|
||||
)
|
||||
) and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
@@ -68,9 +78,13 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
(
|
||||
(
|
||||
tag = CallTag() and
|
||||
result = getInstruction(CallSideEffectTag())
|
||||
if hasSideEffect() then
|
||||
result = getInstruction(CallSideEffectTag())
|
||||
else
|
||||
result = getParent().getChildSuccessor(this)
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
@@ -183,6 +197,18 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
|
||||
predicate hasReadSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
predicate hasWriteSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
private predicate hasSideEffect() {
|
||||
hasReadSideEffect() or hasWriteSideEffect()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +306,14 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
not funcCall.getTarget().(SideEffectFunction).neverReadsMemory()
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
not funcCall.getTarget().(SideEffectFunction).neverWritesMemory()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,4 +336,3 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,10 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
(
|
||||
tag = InitializerVariableAddressTag() or
|
||||
hasUninitializedInstruction() and tag = InitializerStoreTag()
|
||||
) and
|
||||
result = getIRUserVariable(getFunction(), getVariable())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
|
||||
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
|
||||
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
|
||||
|
||||
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockImmediatelyDominates+(dominator, block)
|
||||
}
|
||||
|
||||
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockStrictlyDominates(dominator, block) or dominator = block
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
Graph::Block getDominanceFrontier(Graph::Block dominator) {
|
||||
exists(Graph::Block pred |
|
||||
Graph::blockSuccessor(pred, result) and
|
||||
blockDominates(dominator, pred) and
|
||||
not blockStrictlyDominates(dominator, result)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
private import ReachableBlock as Reachability
|
||||
private module ReachabilityGraph = Reachability::Graph;
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
private import ReachableBlockInternal
|
||||
private import ReachableBlock
|
||||
import IR
|
||||
|
||||
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
(
|
||||
not block instanceof ReachableBlock and
|
||||
key = "Unreachable" and
|
||||
result = "true"
|
||||
) or
|
||||
(
|
||||
exists(EdgeKind kind |
|
||||
isInfeasibleEdge(block, kind) and
|
||||
key = "Infeasible(" + kind.toString() + ")" and
|
||||
result = "true"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
private import ReachableBlockInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
private import ConstantAnalysis
|
||||
|
||||
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
|
||||
exists(ConditionalBranchInstruction instr, int conditionValue |
|
||||
instr = block.getLastInstruction() and
|
||||
conditionValue = getValue(getConstantValue(instr.getCondition())) and
|
||||
if conditionValue = 0 then
|
||||
kind instanceof TrueEdge
|
||||
else
|
||||
kind instanceof FalseEdge
|
||||
)
|
||||
}
|
||||
|
||||
IRBlock getAFeasiblePredecessor(IRBlock successor) {
|
||||
exists(EdgeKind kind |
|
||||
result.getSuccessor(kind) = successor and
|
||||
not isInfeasibleEdge(result, kind)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBlockReachable(IRBlock block) {
|
||||
exists(FunctionIR f |
|
||||
getAFeasiblePredecessor*(block) = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isInstructionReachable(Instruction instr) {
|
||||
isBlockReachable(instr.getBlock())
|
||||
}
|
||||
|
||||
class ReachableBlock extends IRBlock {
|
||||
ReachableBlock() {
|
||||
isBlockReachable(this)
|
||||
}
|
||||
}
|
||||
|
||||
class ReachableInstruction extends Instruction {
|
||||
ReachableInstruction() {
|
||||
this.getBlock() instanceof ReachableBlock
|
||||
}
|
||||
}
|
||||
|
||||
module Graph {
|
||||
predicate isEntryBlock(ReachableBlock block) {
|
||||
exists(FunctionIR f |
|
||||
block = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
|
||||
succ = pred.getASuccessor()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import Cached
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
|
||||
@@ -102,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -603,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1106,9 +1113,39 @@ class CallInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1432,6 +1469,17 @@ class ChiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,13 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
@@ -4,23 +4,35 @@ private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
WrappedInstructionTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
} or
|
||||
ChiTag(OldIR::Instruction oldInstruction) {
|
||||
ChiTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
|
||||
// We need an `Unreached` instruction for the destination of any edge whose predecessor
|
||||
// instruction is reachable, but whose successor block is not. This should occur only for
|
||||
// infeasible edges.
|
||||
exists(OldIR::Instruction succInstruction |
|
||||
succInstruction = oldInstruction.getSuccessor(kind) and
|
||||
not succInstruction instanceof OldInstruction
|
||||
)
|
||||
}
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
@@ -35,11 +47,11 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
cached OldInstruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
|
||||
private Instruction getNewInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewInstruction(OldInstruction instr) {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
@@ -47,21 +59,21 @@ cached private module Cached {
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewFinalInstruction(OldInstruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
|
||||
private ChiInstruction getChiInstruction (OldInstruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
@@ -91,8 +103,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldIR::Instruction instr |
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldInstruction instr |
|
||||
instr.getFunction() = func and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getAST() = ast and
|
||||
@@ -103,7 +115,7 @@ cached private module Cached {
|
||||
else
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
|
||||
exists(OldBlock block, Alias::VirtualVariable vvar |
|
||||
hasPhiNode(vvar, block) and
|
||||
block.getFunction() = func and
|
||||
opcode instanceof Opcode::Phi and
|
||||
@@ -112,7 +124,7 @@ cached private module Cached {
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
|
||||
exists(OldInstruction instr, Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, instr) and
|
||||
instr.getFunction() = func and
|
||||
opcode instanceof Opcode::Chi and
|
||||
@@ -120,11 +132,19 @@ cached private module Cached {
|
||||
tag = ChiTag(instr) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction oldInstruction, EdgeKind kind |
|
||||
oldInstruction.getFunction() = func and
|
||||
tag = UnreachedTag(oldInstruction, kind) and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
ast = oldInstruction.getSuccessor(kind).getAST() and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
|
||||
Type type) {
|
||||
Type type) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getFunction() = func and
|
||||
var.getAST() = ast and
|
||||
@@ -135,19 +155,20 @@ cached private module Cached {
|
||||
|
||||
cached predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
@@ -164,7 +185,7 @@ cached private module Cached {
|
||||
) or
|
||||
// Connect any definitions that are not being modeled in SSA to the
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
exists(OldInstruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
@@ -189,8 +210,8 @@ cached private module Cached {
|
||||
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
|
||||
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
@@ -205,8 +226,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
|
||||
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
|
||||
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
|
||||
int defRank, int defIndex, OldBlock useBlock, int useRank |
|
||||
ChiTag(oldInstr) = chiInstr.getTag() and
|
||||
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
@@ -220,7 +241,7 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldIR::IRBlock oldBlock |
|
||||
exists(OldBlock oldBlock |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
@@ -247,10 +268,11 @@ cached private module Cached {
|
||||
else (
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
) or
|
||||
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -310,29 +332,29 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
|
||||
OldInstruction instr, OldBlock block, int index) {
|
||||
block.getInstruction(index) = instr and
|
||||
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
|
||||
}
|
||||
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
|
||||
(
|
||||
hasPhiNode(vvar, block) and
|
||||
index = -1
|
||||
) or
|
||||
exists(Alias::MemoryAccess access, OldIR::Instruction def |
|
||||
exists(Alias::MemoryAccess access, OldInstruction def |
|
||||
access = Alias::getResultMemoryAccess(def) and
|
||||
block.getInstruction(index) = def and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
|
||||
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
|
||||
}
|
||||
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
|
||||
int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
(
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand())
|
||||
@@ -349,7 +371,7 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
exists (int index | hasUse(vvar, _, block, index) |
|
||||
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
|
||||
) or
|
||||
@@ -357,7 +379,7 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
|
||||
}
|
||||
|
||||
@@ -367,18 +389,18 @@ cached private module Cached {
|
||||
* end of the block, even if the definition is the last instruction in the
|
||||
* block.
|
||||
*/
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
|
||||
}
|
||||
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
int instructionIndex) {
|
||||
hasDefinition(vvar, block, instructionIndex) and
|
||||
defUseRank(vvar, block, rankIndex, instructionIndex)
|
||||
}
|
||||
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
|
||||
int rankIndex, OldIR::Instruction use) {
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
OldInstruction use) {
|
||||
exists(int index |
|
||||
hasUse(vvar, use, block, index) and
|
||||
defUseRank(vvar, block, rankIndex, index)
|
||||
@@ -389,8 +411,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
|
||||
* index `reachesRank` in block `block`.
|
||||
*/
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int defRank, int reachesRank) {
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
|
||||
int reachesRank) {
|
||||
hasDefinitionAtRank(vvar, block, defRank, _) and
|
||||
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
@@ -410,8 +432,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
|
||||
* block `block`.
|
||||
*/
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock block) {
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
(
|
||||
(
|
||||
@@ -421,7 +443,7 @@ cached private module Cached {
|
||||
variableLiveOnExitFromBlock(vvar, defBlock) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
|
||||
) or
|
||||
exists(OldIR::IRBlock idom |
|
||||
exists(OldBlock idom |
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
|
||||
noDefinitionsSinceIDominator(vvar, idom, block)
|
||||
)
|
||||
@@ -429,24 +451,23 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock idom, OldIR::IRBlock block) {
|
||||
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
|
||||
OldBlock block) {
|
||||
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
variableLiveOnExitFromBlock(vvar, block) and
|
||||
not hasDefinition(vvar, block, _)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUseWithinBlock(
|
||||
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
|
||||
OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
defBlock = useBlock and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, useRank)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
(
|
||||
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
|
||||
@@ -459,24 +480,21 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
exists(OldIR::IRBlock defBlock |
|
||||
phiBlock = defBlock.dominanceFrontier() and
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
hasDefinition(vvar, defBlock, _) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
variableLiveOnEntryToBlock(vvar, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction def) {
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
@@ -492,13 +510,17 @@ cached private module CachedForDebugging {
|
||||
}
|
||||
|
||||
cached string getInstructionUniqueId(Instruction instr) {
|
||||
exists(OldIR::Instruction oldInstr |
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
result = "NonSSA: " + oldInstr.getUniqueId()
|
||||
) or
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
|
||||
) or
|
||||
exists(OldInstruction oldInstr, EdgeKind kind |
|
||||
instr.getTag() = UnreachedTag(oldInstr, kind) and
|
||||
result = "Unreached(" + oldInstr.getUniqueId() + ":" + kind.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
|
||||
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
|
||||
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
|
||||
|
||||
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockImmediatelyDominates+(dominator, block)
|
||||
}
|
||||
|
||||
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockStrictlyDominates(dominator, block) or dominator = block
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
Graph::Block getDominanceFrontier(Graph::Block dominator) {
|
||||
exists(Graph::Block pred |
|
||||
Graph::blockSuccessor(pred, result) and
|
||||
blockDominates(dominator, pred) and
|
||||
not blockStrictlyDominates(dominator, result)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
private import ReachableBlock as Reachability
|
||||
private module ReachabilityGraph = Reachability::Graph;
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
private import ReachableBlockInternal
|
||||
private import ReachableBlock
|
||||
import IR
|
||||
|
||||
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
(
|
||||
not block instanceof ReachableBlock and
|
||||
key = "Unreachable" and
|
||||
result = "true"
|
||||
) or
|
||||
(
|
||||
exists(EdgeKind kind |
|
||||
isInfeasibleEdge(block, kind) and
|
||||
key = "Infeasible(" + kind.toString() + ")" and
|
||||
result = "true"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
private import ReachableBlockInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
private import ConstantAnalysis
|
||||
|
||||
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
|
||||
exists(ConditionalBranchInstruction instr, int conditionValue |
|
||||
instr = block.getLastInstruction() and
|
||||
conditionValue = getValue(getConstantValue(instr.getCondition())) and
|
||||
if conditionValue = 0 then
|
||||
kind instanceof TrueEdge
|
||||
else
|
||||
kind instanceof FalseEdge
|
||||
)
|
||||
}
|
||||
|
||||
IRBlock getAFeasiblePredecessor(IRBlock successor) {
|
||||
exists(EdgeKind kind |
|
||||
result.getSuccessor(kind) = successor and
|
||||
not isInfeasibleEdge(result, kind)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBlockReachable(IRBlock block) {
|
||||
exists(FunctionIR f |
|
||||
getAFeasiblePredecessor*(block) = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isInstructionReachable(Instruction instr) {
|
||||
isBlockReachable(instr.getBlock())
|
||||
}
|
||||
|
||||
class ReachableBlock extends IRBlock {
|
||||
ReachableBlock() {
|
||||
isBlockReachable(this)
|
||||
}
|
||||
}
|
||||
|
||||
class ReachableInstruction extends Instruction {
|
||||
ReachableInstruction() {
|
||||
this.getBlock() instanceof ReachableBlock
|
||||
}
|
||||
}
|
||||
|
||||
module Graph {
|
||||
predicate isEntryBlock(ReachableBlock block) {
|
||||
exists(FunctionIR f |
|
||||
block = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
|
||||
succ = pred.getASuccessor()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -102,6 +102,96 @@ IntValue div(IntValue a, IntValue b) {
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a == b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareEQ(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a = b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a != b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareNE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a != b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a < b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a < b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a > b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a > b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a <= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a <= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a >= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a >= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `-a`. If `a` is unknown, the result is unknown.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
private import implementations.Memcpy
|
||||
private import implementations.Printf
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::identity`
|
||||
*/
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
(
|
||||
this.getName() = "move" or
|
||||
this.getName() = "forward"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate neverReadsMemory() {
|
||||
any()
|
||||
}
|
||||
|
||||
override predicate neverWritesMemory() {
|
||||
any()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
// These functions simply return the argument value.
|
||||
index = 0
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
// These functions simply return the argument value.
|
||||
index = 0
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isInParameter(0) and output.isOutReturnValue()
|
||||
}
|
||||
}
|
||||
54
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
54
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate alias modeling of library
|
||||
* functions when source code is not available. To use this QL library,
|
||||
* create a QL class extending `AliasFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by `AliasFunction`
|
||||
* to match the flow within that function.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Models the aliasing behavior of a library function.
|
||||
*/
|
||||
abstract class AliasFunction extends Function {
|
||||
/**
|
||||
* Holds if the address passed to the parameter at the specified index is never retained after
|
||||
* the function returns.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int* g;
|
||||
* int* func(int* p, int* q, int* r, int* s, int n) {
|
||||
* *s = 1; // `s` does not escape.
|
||||
* g = p; // Stored in global. `p` escapes.
|
||||
* if (rand()) {
|
||||
* return q; // `q` escapes via the return value.
|
||||
* }
|
||||
* else {
|
||||
* return r + n; // `r` escapes via the return value, even though an offset has been added.
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For the above function, the following terms hold:
|
||||
* - `parameterEscapesOnlyViaReturn(1)`
|
||||
* - `parameterEscapesOnlyViaReturn(2)`
|
||||
* - `parameterNeverEscapes(3)`
|
||||
*/
|
||||
abstract predicate parameterNeverEscapes(int index);
|
||||
|
||||
/**
|
||||
* Holds if the address passed to the parameter at the specified index escapes via the return
|
||||
* value of the function, but does not otherwise escape. See the comment for
|
||||
* `parameterNeverEscapes` for an example.
|
||||
*/
|
||||
abstract predicate parameterEscapesOnlyViaReturn(int index);
|
||||
|
||||
/**
|
||||
* Holds if the function always returns the value of the parameter at the specified index.
|
||||
*/
|
||||
abstract predicate parameterIsAlwaysReturned(int index);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a set of QL clcasses for indicating dataflows through a particular
|
||||
* Provides a set of QL classes for indicating dataflows through a particular
|
||||
* parameter, return value, or qualifier, as well as flows at one level of
|
||||
* pointer indirection.
|
||||
*/
|
||||
|
||||
30
cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
Normal file
30
cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate dataflow modeling of library
|
||||
* functions when source code is not available. To use this QL library,
|
||||
* create a QL class extending `SideEffectFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by `SideEffectFunction`
|
||||
* to match the flow within that function.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Models the side effects of a library function.
|
||||
*/
|
||||
abstract class SideEffectFunction extends Function {
|
||||
/**
|
||||
* Holds if the function never reads from memory that was defined before entry to the function.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverReadsMemory();
|
||||
|
||||
/**
|
||||
* Holds if the function never writes to memory that remains allocated after the function
|
||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||
* from a pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverWritesMemory();
|
||||
}
|
||||
Reference in New Issue
Block a user