mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'master' into getPhiOperandDefinition-perf-2
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Holds if `e` is either:
|
||||
@@ -64,6 +66,110 @@ int getEffectiveMulOperands(MulExpr me) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* As SimpleRangeAnalysis does not support reasoning about multiplication
|
||||
* we create a tiny abstract interpreter for handling multiplication, which
|
||||
* we invoke only after weeding out of all of trivial cases that we do
|
||||
* not care about. By default, the maximum and minimum values are computed
|
||||
* using SimpleRangeAnalysis.
|
||||
*/
|
||||
class AnalyzableExpr extends Expr {
|
||||
float maxValue() { result = upperBound(this.getFullyConverted()) }
|
||||
|
||||
float minValue() { result = lowerBound(this.getFullyConverted()) }
|
||||
}
|
||||
|
||||
class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr {
|
||||
override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() }
|
||||
|
||||
override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() }
|
||||
}
|
||||
|
||||
class MulAnalyzableExpr extends AnalyzableExpr, MulExpr {
|
||||
override float maxValue() {
|
||||
exists(float x1, float y1, float x2, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(float x1, float x2, float y1, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AddAnalyzableExpr extends AnalyzableExpr, AddExpr {
|
||||
override float maxValue() {
|
||||
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
}
|
||||
|
||||
class SubAnalyzableExpr extends AnalyzableExpr, SubExpr {
|
||||
override float maxValue() {
|
||||
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
}
|
||||
|
||||
class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess {
|
||||
VarAnalyzableExpr() { this.getTarget() instanceof StackVariable }
|
||||
|
||||
override float maxValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
// if there is a defining expression, use that for
|
||||
// computing the maximum value. Otherwise, assign the
|
||||
// variable the largest possible value it can hold
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).maxValue()
|
||||
else result = upperBound(this)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).minValue()
|
||||
else result = lowerBound(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is not an instance of `IntegralType`,
|
||||
* or if `me` cannot be proven to not overflow
|
||||
*/
|
||||
predicate overflows(MulExpr me, Type t) {
|
||||
t instanceof IntegralType
|
||||
implies
|
||||
(
|
||||
me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me)
|
||||
or
|
||||
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
|
||||
)
|
||||
}
|
||||
|
||||
from MulExpr me, Type t1, Type t2
|
||||
where
|
||||
t1 = me.getType().getUnderlyingType() and
|
||||
@@ -101,7 +207,11 @@ where
|
||||
e = other.(BinaryOperation).getAnOperand*()
|
||||
) and
|
||||
e.(Literal).getType().getSize() = t2.getSize()
|
||||
)
|
||||
) and
|
||||
// only report if we cannot prove that the result of the
|
||||
// multiplication will be less (resp. greater) than the
|
||||
// maximum (resp. minimum) number we can compute.
|
||||
overflows(me, t1)
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
||||
@@ -25,10 +25,16 @@ predicate assertInvocation(File f, int line) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(Expr e, Variable v, Declaration qualifier) {
|
||||
nullCheckInCondition(e, v, qualifier) and
|
||||
class InterestingExpr extends Expr {
|
||||
InterestingExpr() { nullCheckInCondition(this, _, _) }
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(InterestingExpr e, Variable v, Declaration qualifier) {
|
||||
exists(File f, int i |
|
||||
e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i)
|
||||
e.getLocation().getStartLine() = i and
|
||||
e.getFile() = f and
|
||||
assertInvocation(f, i) and
|
||||
nullCheckInCondition(e, v, qualifier)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ abstract class BooleanControllingAssignment extends AssignExpr {
|
||||
abstract predicate isWhitelisted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operand of a logical operation expression (we need the restriction
|
||||
* to BinaryLogicalOperation expressions to get the correct transitive closure).
|
||||
*/
|
||||
Expr getComparisonOperand(BinaryLogicalOperation op) { result = op.getAnOperand() }
|
||||
|
||||
class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
BooleanControllingAssignmentInExpr() {
|
||||
this.getParent() instanceof UnaryLogicalOperation or
|
||||
@@ -45,7 +51,18 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
exists(ConditionalExpr c | c.getCondition() = this)
|
||||
}
|
||||
|
||||
override predicate isWhitelisted() { this.getConversion().(ParenthesisExpr).isParenthesised() }
|
||||
override predicate isWhitelisted() {
|
||||
this.getConversion().(ParenthesisExpr).isParenthesised()
|
||||
or
|
||||
// whitelist this assignment if all comparison operations in the expression that this
|
||||
// assignment is part of, are not parenthesized. In that case it seems like programmer
|
||||
// is fine with unparenthesized comparison operands to binary logical operators, and
|
||||
// the parenthesis around this assignment was used to call it out as an assignment.
|
||||
this.isParenthesised() and
|
||||
forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) |
|
||||
not op.isParenthesised()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
@@ -65,7 +82,8 @@ class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
*/
|
||||
predicate candidateResult(BooleanControllingAssignment ae) {
|
||||
ae.getRValue().isConstant() and
|
||||
not ae.isWhitelisted()
|
||||
not ae.isWhitelisted() and
|
||||
not ae.getRValue() instanceof StringLiteral
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,5 +99,6 @@ predicate candidateVariable(Variable v) {
|
||||
from BooleanControllingAssignment ae, UndefReachability undef
|
||||
where
|
||||
candidateResult(ae) and
|
||||
not ae.isFromUninstantiatedTemplate(_) and
|
||||
not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue())
|
||||
select ae, "Use of '=' where '==' may have been intended."
|
||||
|
||||
@@ -16,12 +16,19 @@ import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
|
||||
|
||||
predicate isRandCallOrParent(Expr e) {
|
||||
isRandCall(e) or
|
||||
isRandCallOrParent(e.getAChild())
|
||||
}
|
||||
|
||||
predicate isRandValue(Expr e) {
|
||||
e.(FunctionCall).getTarget().getName() = "rand"
|
||||
isRandCall(e)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
e = mi.getExpr() and
|
||||
e.getAChild*().(FunctionCall).getTarget().getName() = "rand"
|
||||
isRandCallOrParent(e)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,10 @@ predicate assignOperatorWithWrongResult(Operator op, string msg) {
|
||||
|
||||
from Operator op, string msg
|
||||
where
|
||||
assignOperatorWithWrongType(op, msg) or
|
||||
assignOperatorWithWrongResult(op, msg)
|
||||
(
|
||||
assignOperatorWithWrongType(op, msg) or
|
||||
assignOperatorWithWrongResult(op, msg)
|
||||
) and
|
||||
// exclude code in templates which may be incomplete
|
||||
not op.isFromUninstantiatedTemplate(_)
|
||||
select op, msg
|
||||
|
||||
@@ -46,6 +46,10 @@ predicate functionImperfectlyExtracted(Function f) {
|
||||
exists(ErrorExpr ee | ee.getEnclosingFunction() = f)
|
||||
or
|
||||
count(f.getType()) > 1
|
||||
or
|
||||
// an `AsmStmt` isn't strictly 'imperfectly extracted', but it's beyond the scope
|
||||
// of this analysis.
|
||||
exists(AsmStmt asm | asm.getEnclosingFunction() = f)
|
||||
}
|
||||
|
||||
from Stmt stmt, string msg, Function f, ControlFlowNode blame
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -112,9 +112,6 @@ private module ImplCommon {
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
@@ -123,9 +120,9 @@ private module ImplCommon {
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
outercc = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
@@ -156,17 +153,17 @@ private module ImplCommon {
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
result = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
exists(DataFlowCall call, DataFlowCallable callable |
|
||||
result = TSpecificCall(call) and
|
||||
p.isParameterOf(callable, _) and
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
@@ -460,9 +457,6 @@ private module ImplCommon {
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
@@ -471,9 +465,9 @@ private module ImplCommon {
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
outercc = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
@@ -504,17 +498,17 @@ private module ImplCommon {
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
result = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
exists(DataFlowCall call, DataFlowCallable callable |
|
||||
result = TSpecificCall(call) and
|
||||
p.isParameterOf(callable, _) and
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
@@ -579,14 +573,6 @@ private module ImplCommon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||
* expression that reaches a `this` parameter.
|
||||
*/
|
||||
private predicate callHasInstanceArgument(DataFlowCall call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
@@ -601,16 +587,8 @@ private module ImplCommon {
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
recordDataFlowCallSite(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
|
||||
TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or
|
||||
TSomeCall() or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
|
||||
cached
|
||||
@@ -635,11 +613,11 @@ private module ImplCommon {
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
|
||||
* given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable
|
||||
* or helps prune unreachable nodes in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* - `TSomeCall()` : Flow entered through a parameter. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
@@ -663,23 +641,21 @@ private module ImplCommon {
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
}
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||
DataFlowCall getCall() { this = TSpecificCall(result) }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||
exists(ParameterNode p | p.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,7 +824,7 @@ private module ImplCommon {
|
||||
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then result = prunedViableImplInCallContext(call, ctx)
|
||||
else result = viableCallable(call)
|
||||
@@ -861,6 +837,76 @@ private module ImplCommon {
|
||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
newtype TSummary =
|
||||
TSummaryVal() or
|
||||
TSummaryTaint() or
|
||||
TSummaryReadVal(Content f) or
|
||||
TSummaryReadTaint(Content f) or
|
||||
TSummaryTaintStore(Content f)
|
||||
|
||||
/**
|
||||
* A summary of flow through a callable. This can either be value-preserving
|
||||
* if no additional steps are used, taint-flow if at least one additional step
|
||||
* is used, or any one of those combined with a store or a read. Summaries
|
||||
* recorded at a return node are restricted to include at least one additional
|
||||
* step, as the value-based summaries are calculated independent of the
|
||||
* configuration.
|
||||
*/
|
||||
class Summary extends TSummary {
|
||||
string toString() {
|
||||
result = "Val" and this = TSummaryVal()
|
||||
or
|
||||
result = "Taint" and this = TSummaryTaint()
|
||||
or
|
||||
exists(Content f |
|
||||
result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
|
||||
or
|
||||
result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
|
||||
or
|
||||
result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with an additional step. */
|
||||
Summary additionalStep() {
|
||||
this = TSummaryVal() and result = TSummaryTaint()
|
||||
or
|
||||
this = TSummaryTaint() and result = TSummaryTaint()
|
||||
or
|
||||
exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
|
||||
or
|
||||
exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with a read. */
|
||||
Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with a store. */
|
||||
Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with `step`. */
|
||||
bindingset[this, step]
|
||||
Summary compose(Summary step) {
|
||||
this = TSummaryVal() and result = step
|
||||
or
|
||||
this = TSummaryTaint() and
|
||||
(step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
|
||||
result = step
|
||||
or
|
||||
exists(Content f |
|
||||
this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
|
||||
)
|
||||
or
|
||||
this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
|
||||
}
|
||||
|
||||
/** Holds if this summary does not include any taint steps. */
|
||||
predicate isPartial() {
|
||||
this = TSummaryVal() or
|
||||
this = TSummaryReadVal(_)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowType getErasedNodeType(Node n) { result = getErasedRepr(n.getType()) }
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -112,9 +112,6 @@ private module ImplCommon {
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
@@ -123,9 +120,9 @@ private module ImplCommon {
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
outercc = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
@@ -156,17 +153,17 @@ private module ImplCommon {
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
result = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
exists(DataFlowCall call, DataFlowCallable callable |
|
||||
result = TSpecificCall(call) and
|
||||
p.isParameterOf(callable, _) and
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
@@ -460,9 +457,6 @@ private module ImplCommon {
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
@@ -471,9 +465,9 @@ private module ImplCommon {
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
outercc = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
@@ -504,17 +498,17 @@ private module ImplCommon {
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
then innercc = TSpecificCall(call)
|
||||
else innercc = TSomeCall()
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
result = TSomeCall()
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
exists(DataFlowCall call, DataFlowCallable callable |
|
||||
result = TSpecificCall(call) and
|
||||
p.isParameterOf(callable, _) and
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
@@ -579,14 +573,6 @@ private module ImplCommon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||
* expression that reaches a `this` parameter.
|
||||
*/
|
||||
private predicate callHasInstanceArgument(DataFlowCall call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
@@ -601,16 +587,8 @@ private module ImplCommon {
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
recordDataFlowCallSite(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
|
||||
TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or
|
||||
TSomeCall() or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
|
||||
cached
|
||||
@@ -635,11 +613,11 @@ private module ImplCommon {
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
|
||||
* given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable
|
||||
* or helps prune unreachable nodes in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* - `TSomeCall()` : Flow entered through a parameter. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
@@ -663,23 +641,21 @@ private module ImplCommon {
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
}
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||
DataFlowCall getCall() { this = TSpecificCall(result) }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||
exists(ParameterNode p | p.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,7 +824,7 @@ private module ImplCommon {
|
||||
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then result = prunedViableImplInCallContext(call, ctx)
|
||||
else result = viableCallable(call)
|
||||
@@ -861,6 +837,76 @@ private module ImplCommon {
|
||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
newtype TSummary =
|
||||
TSummaryVal() or
|
||||
TSummaryTaint() or
|
||||
TSummaryReadVal(Content f) or
|
||||
TSummaryReadTaint(Content f) or
|
||||
TSummaryTaintStore(Content f)
|
||||
|
||||
/**
|
||||
* A summary of flow through a callable. This can either be value-preserving
|
||||
* if no additional steps are used, taint-flow if at least one additional step
|
||||
* is used, or any one of those combined with a store or a read. Summaries
|
||||
* recorded at a return node are restricted to include at least one additional
|
||||
* step, as the value-based summaries are calculated independent of the
|
||||
* configuration.
|
||||
*/
|
||||
class Summary extends TSummary {
|
||||
string toString() {
|
||||
result = "Val" and this = TSummaryVal()
|
||||
or
|
||||
result = "Taint" and this = TSummaryTaint()
|
||||
or
|
||||
exists(Content f |
|
||||
result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
|
||||
or
|
||||
result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
|
||||
or
|
||||
result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with an additional step. */
|
||||
Summary additionalStep() {
|
||||
this = TSummaryVal() and result = TSummaryTaint()
|
||||
or
|
||||
this = TSummaryTaint() and result = TSummaryTaint()
|
||||
or
|
||||
exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
|
||||
or
|
||||
exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with a read. */
|
||||
Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with a store. */
|
||||
Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with `step`. */
|
||||
bindingset[this, step]
|
||||
Summary compose(Summary step) {
|
||||
this = TSummaryVal() and result = step
|
||||
or
|
||||
this = TSummaryTaint() and
|
||||
(step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
|
||||
result = step
|
||||
or
|
||||
exists(Content f |
|
||||
this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
|
||||
)
|
||||
or
|
||||
this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
|
||||
}
|
||||
|
||||
/** Holds if this summary does not include any taint steps. */
|
||||
predicate isPartial() {
|
||||
this = TSummaryVal() or
|
||||
this = TSummaryReadVal(_)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowType getErasedNodeType(Node n) { result = getErasedRepr(n.getType()) }
|
||||
|
||||
|
||||
@@ -59,10 +59,12 @@ class Node extends TIRDataFlowNode {
|
||||
Parameter asParameter() { result = instr.(InitializeParameterInstruction).getParameter() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: See UninitializedNode.
|
||||
*
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
*/
|
||||
LocalVariable asUninitialized() { result = instr.(UninitializedInstruction).getLocalVariable() }
|
||||
LocalVariable asUninitialized() { none() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
@@ -140,15 +142,19 @@ private class ThisParameterNode extends Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Data flow was never an accurate way to determine what
|
||||
* expressions might be uninitialized. It errs on the side of saying that
|
||||
* everything is uninitialized, and this is even worse in the IR because the IR
|
||||
* doesn't use syntactic hints to rule out variables that are definitely
|
||||
* initialized.
|
||||
*
|
||||
* The value of an uninitialized local variable, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class UninitializedNode extends Node {
|
||||
override UninitializedInstruction instr;
|
||||
deprecated class UninitializedNode extends Node {
|
||||
UninitializedNode() { none() }
|
||||
|
||||
LocalVariable getLocalVariable() { result = instr.getLocalVariable() }
|
||||
|
||||
override string toString() { result = this.getLocalVariable().toString() }
|
||||
LocalVariable getLocalVariable() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +265,21 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom or
|
||||
// Treat all conversions as flow, even conversions between different numeric types.
|
||||
iTo.(ConvertInstruction).getUnary() = iFrom or
|
||||
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
|
||||
iTo.(InheritanceConversionInstruction).getUnary() = iFrom or
|
||||
// A chi instruction represents a point where a new value (the _partial_
|
||||
// operand) may overwrite an old value (the _total_ operand), but the alias
|
||||
// analysis couldn't determine that it surely will overwrite every bit of it or
|
||||
// that it surely will overwrite no bit of it.
|
||||
//
|
||||
// By allowing flow through the total operand, we ensure that flow is not lost
|
||||
// due to shortcomings of the alias analysis. We may get false flow in cases
|
||||
// where the data is indeed overwritten.
|
||||
//
|
||||
// Allowing flow through the partial operand would be more noisy, especially
|
||||
// for variables that have escaped: for soundness, the IR has to assume that
|
||||
// every write to an unknown address can affect every escaped variable, and
|
||||
// this assumption shows up as data flowing through partial chi operands.
|
||||
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
private import internal.OpcodeImports as Imports
|
||||
private import internal.OperandTag
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TOpcode =
|
||||
TNoOp() or
|
||||
TUninitialized() or
|
||||
@@ -84,11 +88,67 @@ private newtype TOpcode =
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() { result = "UnknownOpcode" }
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by this instruction's result.
|
||||
* Holds only for opcodes with a memory result.
|
||||
*/
|
||||
MemoryAccessKind getWriteMemoryAccess() { none() }
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by this instruction's `MemoryOperand`. Holds only for
|
||||
* opcodes that read from memory.
|
||||
*/
|
||||
MemoryAccessKind getReadMemoryAccess() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction has an `AddressOperand`.
|
||||
*/
|
||||
predicate hasAddressOperand() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction has a `BufferSizeOperand`.
|
||||
*/
|
||||
predicate hasBufferSizeOperand() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction's write memory access is a `may` write, as opposed to a `must` write.
|
||||
*/
|
||||
predicate hasMayWriteMemoryAccess() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction's read memory access is a `may` read, as opposed to a `must` read.
|
||||
*/
|
||||
predicate hasMayReadMemoryAccess() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction must have an operand with the specified `OperandTag`.
|
||||
*/
|
||||
final predicate hasOperand(OperandTag tag) {
|
||||
hasOperandInternal(tag)
|
||||
or
|
||||
hasAddressOperand() and tag instanceof AddressOperandTag
|
||||
or
|
||||
hasBufferSizeOperand() and tag instanceof BufferSizeOperandTag
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the instruction must have an operand with the specified `OperandTag`, ignoring
|
||||
* `AddressOperandTag` and `BufferSizeOperandTag`.
|
||||
*/
|
||||
predicate hasOperandInternal(OperandTag tag) { none() }
|
||||
}
|
||||
|
||||
abstract class UnaryOpcode extends Opcode { }
|
||||
abstract class UnaryOpcode extends Opcode {
|
||||
final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag }
|
||||
}
|
||||
|
||||
abstract class BinaryOpcode extends Opcode { }
|
||||
abstract class BinaryOpcode extends Opcode {
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PointerArithmeticOpcode extends BinaryOpcode { }
|
||||
|
||||
@@ -122,55 +182,138 @@ abstract class ThrowOpcode extends Opcode { }
|
||||
|
||||
abstract class CatchOpcode extends Opcode { }
|
||||
|
||||
abstract class OpcodeWithCondition extends Opcode { }
|
||||
abstract class OpcodeWithCondition extends Opcode {
|
||||
final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag }
|
||||
}
|
||||
|
||||
abstract class BuiltInOperationOpcode extends Opcode { }
|
||||
|
||||
abstract class SideEffectOpcode extends Opcode { }
|
||||
|
||||
/**
|
||||
* An opcode that accesses a single memory location via an `AddressOperand`.
|
||||
*/
|
||||
abstract class IndirectMemoryAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a single memory location via an `AddressOperand`.
|
||||
*/
|
||||
abstract class IndirectWriteOpcode extends IndirectMemoryAccessOpcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a single memory location via an `AddressOperand`.
|
||||
*/
|
||||
abstract class IndirectReadOpcode extends IndirectMemoryAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class BufferWriteOpcode extends BufferAccessOpcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class BufferReadOpcode extends BufferAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class SizedBufferAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
|
||||
final override predicate hasBufferSizeOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a memory buffer whose size is determined by a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class SizedBufferWriteOpcode extends SizedBufferAccessOpcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() {
|
||||
result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a memory buffer whose size is determined by a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class SizedBufferReadOpcode extends SizedBufferAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() {
|
||||
result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that might write to any escaped memory location.
|
||||
*/
|
||||
abstract class EscapedWriteOpcode extends Opcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that might read from any escaped memory location.
|
||||
*/
|
||||
abstract class EscapedReadOpcode extends Opcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode whose write memory access is a `may` write, as opposed to a `must` write.
|
||||
*/
|
||||
abstract class MayWriteOpcode extends Opcode {
|
||||
final override predicate hasMayWriteMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode whose read memory access is a `may` read, as opposed to a `must` read.
|
||||
*/
|
||||
abstract class MayReadOpcode extends Opcode {
|
||||
final override predicate hasMayReadMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads a value from memory.
|
||||
*/
|
||||
abstract class OpcodeWithLoad extends MemoryAccessOpcode { }
|
||||
abstract class OpcodeWithLoad extends IndirectReadOpcode {
|
||||
final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LoadOperandTag }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class ReadSideEffectOpcode extends SideEffectOpcode { }
|
||||
abstract class ReadSideEffectOpcode extends SideEffectOpcode {
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof SideEffectOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that definitely writes to a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class MustWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
|
||||
* as a read of the original contents, plus a "may" write of the new contents.
|
||||
*/
|
||||
abstract class MayWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand`.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends MemoryAccessOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand` with a `BufferSizeOperand` specifying
|
||||
* the number of elements accessed.
|
||||
*/
|
||||
abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { }
|
||||
|
||||
module Opcode {
|
||||
class NoOp extends Opcode, TNoOp {
|
||||
final override string toString() { result = "NoOp" }
|
||||
}
|
||||
|
||||
class Uninitialized extends MemoryAccessOpcode, TUninitialized {
|
||||
class Uninitialized extends IndirectWriteOpcode, TUninitialized {
|
||||
final override string toString() { result = "Uninitialized" }
|
||||
}
|
||||
|
||||
@@ -178,11 +321,11 @@ module Opcode {
|
||||
final override string toString() { result = "Error" }
|
||||
}
|
||||
|
||||
class InitializeParameter extends MemoryAccessOpcode, TInitializeParameter {
|
||||
class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter {
|
||||
final override string toString() { result = "InitializeParameter" }
|
||||
}
|
||||
|
||||
class InitializeIndirection extends MemoryAccessOpcode, TInitializeIndirection {
|
||||
class InitializeIndirection extends IndirectWriteOpcode, TInitializeIndirection {
|
||||
final override string toString() { result = "InitializeIndirection" }
|
||||
}
|
||||
|
||||
@@ -206,8 +349,12 @@ module Opcode {
|
||||
final override string toString() { result = "ReturnVoid" }
|
||||
}
|
||||
|
||||
class ReturnIndirection extends MemoryAccessOpcode, TReturnIndirection {
|
||||
class ReturnIndirection extends IndirectReadOpcode, TReturnIndirection {
|
||||
final override string toString() { result = "ReturnIndirection" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof SideEffectOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue {
|
||||
@@ -218,8 +365,12 @@ module Opcode {
|
||||
final override string toString() { result = "Load" }
|
||||
}
|
||||
|
||||
class Store extends CopyOpcode, MemoryAccessOpcode, TStore {
|
||||
class Store extends CopyOpcode, IndirectWriteOpcode, TStore {
|
||||
final override string toString() { result = "Store" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof StoreValueOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class Add extends BinaryArithmeticOpcode, TAdd {
|
||||
@@ -372,6 +523,10 @@ module Opcode {
|
||||
|
||||
class Call extends Opcode, TCall {
|
||||
final override string toString() { result = "Call" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof CallTargetOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class CatchByType extends CatchOpcode, TCatchByType {
|
||||
@@ -396,22 +551,40 @@ module Opcode {
|
||||
|
||||
class UnmodeledDefinition extends Opcode, TUnmodeledDefinition {
|
||||
final override string toString() { result = "UnmodeledDefinition" }
|
||||
|
||||
final override MemoryAccessKind getWriteMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class UnmodeledUse extends Opcode, TUnmodeledUse {
|
||||
final override string toString() { result = "UnmodeledUse" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof UnmodeledUseOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class AliasedDefinition extends Opcode, TAliasedDefinition {
|
||||
final override string toString() { result = "AliasedDefinition" }
|
||||
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
class AliasedUse extends Opcode, TAliasedUse {
|
||||
final override string toString() { result = "AliasedUse" }
|
||||
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof NonLocalMemoryAccess }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof SideEffectOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class Phi extends Opcode, TPhi {
|
||||
final override string toString() { result = "Phi" }
|
||||
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
}
|
||||
|
||||
class BuiltIn extends BuiltInOperationOpcode, TBuiltIn {
|
||||
@@ -434,64 +607,81 @@ module Opcode {
|
||||
final override string toString() { result = "VarArgCopy" }
|
||||
}
|
||||
|
||||
class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect {
|
||||
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,
|
||||
ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect {
|
||||
final override string toString() { result = "CallSideEffect" }
|
||||
}
|
||||
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect {
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode,
|
||||
TCallReadSideEffect {
|
||||
final override string toString() { result = "CallReadSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode,
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode,
|
||||
TIndirectReadSideEffect {
|
||||
final override string toString() { result = "IndirectReadSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectMustWriteSideEffect extends MustWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
|
||||
TIndirectMustWriteSideEffect {
|
||||
final override string toString() { result = "IndirectMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
TIndirectMayWriteSideEffect {
|
||||
class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
|
||||
MayWriteOpcode, TIndirectMayWriteSideEffect {
|
||||
final override string toString() { result = "IndirectMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect {
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferReadOpcode, TBufferReadSideEffect {
|
||||
final override string toString() { result = "BufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMustWriteSideEffect extends MustWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
class BufferMustWriteSideEffect extends WriteSideEffectOpcode, BufferWriteOpcode,
|
||||
TBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
class BufferMayWriteSideEffect extends WriteSideEffectOpcode, BufferWriteOpcode, MayWriteOpcode,
|
||||
TBufferMayWriteSideEffect {
|
||||
final override string toString() { result = "BufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode,
|
||||
TSizedBufferReadSideEffect {
|
||||
final override string toString() { result = "SizedBufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferMustWriteSideEffect extends MustWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
|
||||
TSizedBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "SizedBufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferMayWriteSideEffect extends MayWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
TSizedBufferMayWriteSideEffect {
|
||||
class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
|
||||
MayWriteOpcode, TSizedBufferMayWriteSideEffect {
|
||||
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class Chi extends Opcode, TChi {
|
||||
final override string toString() { result = "Chi" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof ChiTotalOperandTag
|
||||
or
|
||||
tag instanceof ChiPartialOperandTag
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getWriteMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class InlineAsm extends Opcode, TInlineAsm {
|
||||
class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode,
|
||||
MayReadOpcode, TInlineAsm {
|
||||
final override string toString() { result = "InlineAsm" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
tag instanceof SideEffectOperandTag
|
||||
}
|
||||
}
|
||||
|
||||
class Unreached extends Opcode, TUnreached {
|
||||
|
||||
@@ -11,60 +11,12 @@ import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if the instruction `instr` should be expected to have an operand
|
||||
* with operand tag `tag`. Only holds for singleton operand tags. Tags with
|
||||
* parameters, such as `PhiInputOperand` and `PositionalArgumentOperand` are handled
|
||||
* separately in `unexpectedOperand`.
|
||||
*/
|
||||
private predicate expectsOperand(Instruction instr, OperandTag tag) {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag
|
||||
or
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithLoad and tag instanceof LoadOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Store and tag instanceof StoreValueOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
expectsOperand(instr, tag) and
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
@@ -84,7 +36,7 @@ module InstructionSanity {
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
@@ -308,6 +260,19 @@ module InstructionSanity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
file = location.getFile() and
|
||||
line = location.getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
@@ -372,8 +337,8 @@ class Instruction extends Construction::TInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this = rank[result](Instruction instr |
|
||||
instr.getAST().getFile() = getAST().getFile() and
|
||||
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
||||
instr = getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(),
|
||||
getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
@@ -549,7 +514,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the kind of memory access performed by this instruction's result.
|
||||
* Holds only for instructions with a memory result.
|
||||
*/
|
||||
MemoryAccessKind getResultMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this instruction's result will not always write to
|
||||
@@ -559,7 +525,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* location is a conservative estimate of the memory that might actually be accessed at runtime
|
||||
* (for example, the global side effects of a function call).
|
||||
*/
|
||||
predicate hasResultMayMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Gets the operand that holds the memory address to which this instruction stores its
|
||||
@@ -710,16 +677,12 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -754,8 +717,6 @@ class ErrorInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the variable that is uninitialized.
|
||||
*/
|
||||
@@ -821,8 +782,6 @@ class LoadInstruction extends CopyInstruction {
|
||||
class StoreInstruction extends CopyInstruction {
|
||||
StoreInstruction() { getOpcode() instanceof Opcode::Store }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
|
||||
@@ -1236,10 +1195,6 @@ class SideEffectInstruction extends Instruction {
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1303,8 +1258,6 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1315,8 +1268,6 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1328,8 +1279,6 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1342,10 +1291,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1354,10 +1299,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1369,10 +1310,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1381,10 +1318,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
*/
|
||||
class InlineAsmInstruction extends Instruction {
|
||||
InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1470,10 +1403,6 @@ class CatchAnyInstruction extends CatchInstruction {
|
||||
|
||||
class UnmodeledDefinitionInstruction extends Instruction {
|
||||
UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1481,8 +1410,6 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1511,8 +1438,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() { getOpcode() instanceof Opcode::Phi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block.
|
||||
*/
|
||||
@@ -1573,8 +1498,6 @@ class PhiInstruction extends Instruction {
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() { getOpcode() instanceof Opcode::Chi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof ChiTotalMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
|
||||
@@ -170,7 +170,7 @@ class MemoryOperand extends Operand {
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() { none() }
|
||||
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this operand will not always read from every bit in the
|
||||
@@ -180,7 +180,7 @@ class MemoryOperand extends Operand {
|
||||
* conservative estimate of the memory that might actually be accessed at runtime (for example,
|
||||
* the global side effects of a function call).
|
||||
*/
|
||||
predicate hasMayMemoryAccess() { none() }
|
||||
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
@@ -274,8 +274,6 @@ class LoadOperand extends TypedOperand {
|
||||
override LoadOperandTag tag;
|
||||
|
||||
override string toString() { result = "Load" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,8 +329,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
|
||||
override UnmodeledUseOperandTag tag;
|
||||
|
||||
override string toString() { result = "UnmodeledUse" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof UnmodeledMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,50 +378,6 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof ReturnIndirectionInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
final override predicate hasMayMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ private predicate hasOperandMemoryAccess(
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
(if operand.hasMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
@@ -41,8 +41,18 @@ private newtype TMemoryLocation =
|
||||
IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
(
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess) or
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
or
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
or
|
||||
exists(IRAutomaticVariable autoVar |
|
||||
// Always create a memory location for the entire variable.
|
||||
autoVar = var and
|
||||
type = autoVar.getIRType() and
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = type.getByteSize() * 8 and
|
||||
isMayAccess = false
|
||||
)
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
@@ -78,6 +88,8 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract IRFunction getIRFunction();
|
||||
|
||||
abstract Location getLocation();
|
||||
|
||||
final IRType getIRType() { result = getType().getIRType() }
|
||||
|
||||
abstract predicate isMayAccess();
|
||||
@@ -141,6 +153,8 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
|
||||
final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final IntValue getStartBitOffset() { result = startBitOffset }
|
||||
|
||||
final IntValue getEndBitOffset() { result = endBitOffset }
|
||||
@@ -208,6 +222,8 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
|
||||
final override IRFunction getIRFunction() { result = irFunc }
|
||||
|
||||
final override Location getLocation() { result = irFunc.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = "{Unknown}" }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
@@ -233,6 +249,8 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
|
||||
|
||||
final override IRFunction getIRFunction() { result = irFunc }
|
||||
|
||||
final override Location getLocation() { result = irFunc.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = "{AllNonLocal}" }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
@@ -255,6 +273,8 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
|
||||
|
||||
final override IRFunction getIRFunction() { result = irFunc }
|
||||
|
||||
final override Location getLocation() { result = irFunc.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = " " + toString() }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TAllAliasedMemory(irFunc, false) }
|
||||
@@ -401,50 +421,36 @@ private predicate isRelatableMemoryLocation(VariableMemoryLocation vml) {
|
||||
vml.getStartBitOffset() != Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate isCoveredOffset(
|
||||
VirtualVariable vv, IRVariable var, int offsetRank, VariableMemoryLocation vml
|
||||
) {
|
||||
exists(int startRank, int endRank |
|
||||
vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vv, offset_)) and
|
||||
vml.getEndBitOffset() = rank[endRank](IntValue offset_ | isRelevantOffset(vv, offset_)) and
|
||||
hasVariableAndVirtualVariable(vv, var, vml) and
|
||||
private predicate isCoveredOffset(IRVariable var, int offsetRank, VariableMemoryLocation vml) {
|
||||
exists(int startRank, int endRank, VirtualVariable vvar |
|
||||
vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
vml.getEndBitOffset() = rank[endRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
var = vml.getVariable() and
|
||||
vvar = vml.getVirtualVariable() and
|
||||
isRelatableMemoryLocation(vml) and
|
||||
offsetRank in [startRank .. endRank]
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasUnknownOffset(VirtualVariable vv, IRVariable var, VariableMemoryLocation vml) {
|
||||
hasVariableAndVirtualVariable(vv, var, vml) and
|
||||
private predicate hasUnknownOffset(IRVariable var, VariableMemoryLocation vml) {
|
||||
vml.getVariable() = var and
|
||||
(
|
||||
vml.getStartBitOffset() = Ints::unknown() or
|
||||
vml.getEndBitOffset() = Ints::unknown()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasVariableAndVirtualVariable(
|
||||
VirtualVariable vv, IRVariable var, VariableMemoryLocation vml
|
||||
) {
|
||||
var = vml.getVariable() and
|
||||
vv = vml.getVirtualVariable()
|
||||
}
|
||||
|
||||
private predicate overlappingIRVariableMemoryLocations(
|
||||
VariableMemoryLocation def, VariableMemoryLocation use
|
||||
) {
|
||||
exists(VirtualVariable vv, IRVariable var, int offsetRank |
|
||||
isCoveredOffset(vv, var, offsetRank, def) and
|
||||
isCoveredOffset(vv, var, offsetRank, use)
|
||||
exists(IRVariable var, int offsetRank |
|
||||
isCoveredOffset(var, offsetRank, def) and
|
||||
isCoveredOffset(var, offsetRank, use)
|
||||
)
|
||||
or
|
||||
exists(VirtualVariable vv, IRVariable var |
|
||||
hasUnknownOffset(vv, var, def) and
|
||||
hasVariableAndVirtualVariable(vv, var, use)
|
||||
)
|
||||
hasUnknownOffset(use.getVariable(), def)
|
||||
or
|
||||
exists(VirtualVariable vv, IRVariable var |
|
||||
hasUnknownOffset(vv, var, use) and
|
||||
hasVariableAndVirtualVariable(vv, var, def)
|
||||
)
|
||||
hasUnknownOffset(def.getVariable(), use)
|
||||
}
|
||||
|
||||
private Overlap getVariableMemoryLocationOverlap(
|
||||
@@ -483,7 +489,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
kind = operand.getMemoryAccess() and
|
||||
(if operand.hasMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
|
||||
@@ -891,4 +891,13 @@ module SSASanity {
|
||||
message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingVirtualVariableForMemoryLocation(
|
||||
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
|
||||
) {
|
||||
not exists(location.getVirtualVariable()) and
|
||||
func = location.getIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction()) and
|
||||
message = "Memory location has no virtual variable in function '$@'."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
@@ -11,60 +11,12 @@ import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if the instruction `instr` should be expected to have an operand
|
||||
* with operand tag `tag`. Only holds for singleton operand tags. Tags with
|
||||
* parameters, such as `PhiInputOperand` and `PositionalArgumentOperand` are handled
|
||||
* separately in `unexpectedOperand`.
|
||||
*/
|
||||
private predicate expectsOperand(Instruction instr, OperandTag tag) {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag
|
||||
or
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithLoad and tag instanceof LoadOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Store and tag instanceof StoreValueOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
expectsOperand(instr, tag) and
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
@@ -84,7 +36,7 @@ module InstructionSanity {
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
@@ -308,6 +260,19 @@ module InstructionSanity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
file = location.getFile() and
|
||||
line = location.getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
@@ -372,8 +337,8 @@ class Instruction extends Construction::TInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this = rank[result](Instruction instr |
|
||||
instr.getAST().getFile() = getAST().getFile() and
|
||||
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
||||
instr = getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(),
|
||||
getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
@@ -549,7 +514,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the kind of memory access performed by this instruction's result.
|
||||
* Holds only for instructions with a memory result.
|
||||
*/
|
||||
MemoryAccessKind getResultMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this instruction's result will not always write to
|
||||
@@ -559,7 +525,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* location is a conservative estimate of the memory that might actually be accessed at runtime
|
||||
* (for example, the global side effects of a function call).
|
||||
*/
|
||||
predicate hasResultMayMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Gets the operand that holds the memory address to which this instruction stores its
|
||||
@@ -710,16 +677,12 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -754,8 +717,6 @@ class ErrorInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the variable that is uninitialized.
|
||||
*/
|
||||
@@ -821,8 +782,6 @@ class LoadInstruction extends CopyInstruction {
|
||||
class StoreInstruction extends CopyInstruction {
|
||||
StoreInstruction() { getOpcode() instanceof Opcode::Store }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
|
||||
@@ -1236,10 +1195,6 @@ class SideEffectInstruction extends Instruction {
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1303,8 +1258,6 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1315,8 +1268,6 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1328,8 +1279,6 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1342,10 +1291,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1354,10 +1299,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1369,10 +1310,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1381,10 +1318,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
*/
|
||||
class InlineAsmInstruction extends Instruction {
|
||||
InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1470,10 +1403,6 @@ class CatchAnyInstruction extends CatchInstruction {
|
||||
|
||||
class UnmodeledDefinitionInstruction extends Instruction {
|
||||
UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1481,8 +1410,6 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1511,8 +1438,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() { getOpcode() instanceof Opcode::Phi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block.
|
||||
*/
|
||||
@@ -1573,8 +1498,6 @@ class PhiInstruction extends Instruction {
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() { getOpcode() instanceof Opcode::Chi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof ChiTotalMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
|
||||
@@ -170,7 +170,7 @@ class MemoryOperand extends Operand {
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() { none() }
|
||||
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this operand will not always read from every bit in the
|
||||
@@ -180,7 +180,7 @@ class MemoryOperand extends Operand {
|
||||
* conservative estimate of the memory that might actually be accessed at runtime (for example,
|
||||
* the global side effects of a function call).
|
||||
*/
|
||||
predicate hasMayMemoryAccess() { none() }
|
||||
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
@@ -274,8 +274,6 @@ class LoadOperand extends TypedOperand {
|
||||
override LoadOperandTag tag;
|
||||
|
||||
override string toString() { result = "Load" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,8 +329,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
|
||||
override UnmodeledUseOperandTag tag;
|
||||
|
||||
override string toString() { result = "UnmodeledUse" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof UnmodeledMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,50 +378,6 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof ReturnIndirectionInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
final override predicate hasMayMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,60 +11,12 @@ import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if the instruction `instr` should be expected to have an operand
|
||||
* with operand tag `tag`. Only holds for singleton operand tags. Tags with
|
||||
* parameters, such as `PhiInputOperand` and `PositionalArgumentOperand` are handled
|
||||
* separately in `unexpectedOperand`.
|
||||
*/
|
||||
private predicate expectsOperand(Instruction instr, OperandTag tag) {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag
|
||||
or
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithLoad and tag instanceof LoadOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Store and tag instanceof StoreValueOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag
|
||||
or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
expectsOperand(instr, tag) and
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
@@ -84,7 +36,7 @@ module InstructionSanity {
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
@@ -308,6 +260,19 @@ module InstructionSanity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
file = location.getFile() and
|
||||
line = location.getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
@@ -372,8 +337,8 @@ class Instruction extends Construction::TInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this = rank[result](Instruction instr |
|
||||
instr.getAST().getFile() = getAST().getFile() and
|
||||
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
||||
instr = getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(),
|
||||
getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
@@ -549,7 +514,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the kind of memory access performed by this instruction's result.
|
||||
* Holds only for instructions with a memory result.
|
||||
*/
|
||||
MemoryAccessKind getResultMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this instruction's result will not always write to
|
||||
@@ -559,7 +525,8 @@ class Instruction extends Construction::TInstruction {
|
||||
* location is a conservative estimate of the memory that might actually be accessed at runtime
|
||||
* (for example, the global side effects of a function call).
|
||||
*/
|
||||
predicate hasResultMayMemoryAccess() { none() }
|
||||
pragma[inline]
|
||||
final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Gets the operand that holds the memory address to which this instruction stores its
|
||||
@@ -710,16 +677,12 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
|
||||
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -754,8 +717,6 @@ class ErrorInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the variable that is uninitialized.
|
||||
*/
|
||||
@@ -821,8 +782,6 @@ class LoadInstruction extends CopyInstruction {
|
||||
class StoreInstruction extends CopyInstruction {
|
||||
StoreInstruction() { getOpcode() instanceof Opcode::Store }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
|
||||
@@ -1236,10 +1195,6 @@ class SideEffectInstruction extends Instruction {
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1303,8 +1258,6 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1315,8 +1268,6 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1328,8 +1279,6 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1342,10 +1291,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1354,10 +1299,6 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1369,10 +1310,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
@@ -1381,10 +1318,6 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
*/
|
||||
class InlineAsmInstruction extends Instruction {
|
||||
InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
|
||||
final override predicate hasResultMayMemoryAccess() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1470,10 +1403,6 @@ class CatchAnyInstruction extends CatchInstruction {
|
||||
|
||||
class UnmodeledDefinitionInstruction extends Instruction {
|
||||
UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1481,8 +1410,6 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1511,8 +1438,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() { getOpcode() instanceof Opcode::Phi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block.
|
||||
*/
|
||||
@@ -1573,8 +1498,6 @@ class PhiInstruction extends Instruction {
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() { getOpcode() instanceof Opcode::Chi }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof ChiTotalMemoryAccess }
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
|
||||
@@ -170,7 +170,7 @@ class MemoryOperand extends Operand {
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() { none() }
|
||||
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this operand will not always read from every bit in the
|
||||
@@ -180,7 +180,7 @@ class MemoryOperand extends Operand {
|
||||
* conservative estimate of the memory that might actually be accessed at runtime (for example,
|
||||
* the global side effects of a function call).
|
||||
*/
|
||||
predicate hasMayMemoryAccess() { none() }
|
||||
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
@@ -274,8 +274,6 @@ class LoadOperand extends TypedOperand {
|
||||
override LoadOperandTag tag;
|
||||
|
||||
override string toString() { result = "Load" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,8 +329,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
|
||||
override UnmodeledUseOperandTag tag;
|
||||
|
||||
override string toString() { result = "UnmodeledUse" }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof UnmodeledMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,50 +378,6 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof ReturnIndirectionInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
final override predicate hasMayMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -891,4 +891,13 @@ module SSASanity {
|
||||
message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingVirtualVariableForMemoryLocation(
|
||||
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
|
||||
) {
|
||||
not exists(location.getVirtualVariable()) and
|
||||
func = location.getIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction()) and
|
||||
message = "Memory location has no virtual variable in function '$@'."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ private predicate isVariableModeled(IRVariable var) {
|
||||
|
|
||||
bitOffset = 0 and
|
||||
type.getIRType() = var.getIRType() and
|
||||
not operand.hasMayMemoryAccess()
|
||||
not operand.hasMayReadMemoryAccess()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
final string toString() { result = var.toString() }
|
||||
|
||||
final Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
|
||||
final VirtualVariable getVirtualVariable() { result = this }
|
||||
|
||||
@@ -13,6 +13,8 @@ class Function = Cpp::Function;
|
||||
|
||||
class Location = Cpp::Location;
|
||||
|
||||
class File = Cpp::File;
|
||||
|
||||
class AST = Cpp::Locatable;
|
||||
|
||||
class Type = Cpp::Type;
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
| ref.cpp:109:15:109:20 | ref.cpp:132:13:132:15 | AST only |
|
||||
| ref.cpp:122:23:122:28 | ref.cpp:123:13:123:15 | AST only |
|
||||
| ref.cpp:125:19:125:24 | ref.cpp:126:13:126:15 | AST only |
|
||||
| test.cpp:75:7:75:8 | test.cpp:76:8:76:9 | AST only |
|
||||
| test.cpp:83:7:83:8 | test.cpp:84:8:84:18 | AST only |
|
||||
| test.cpp:83:7:83:8 | test.cpp:86:8:86:9 | AST only |
|
||||
| test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only |
|
||||
| test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only |
|
||||
| test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | IR only |
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
| test.cpp:31:8:31:8 | c | test.cpp:36:13:36:18 | call to source |
|
||||
| test.cpp:58:10:58:10 | t | test.cpp:50:14:50:19 | call to source |
|
||||
| test.cpp:71:8:71:9 | x4 | test.cpp:66:30:66:36 | source1 |
|
||||
| test.cpp:76:8:76:9 | u1 | test.cpp:75:7:75:8 | u1 |
|
||||
| test.cpp:84:8:84:18 | ... ? ... : ... | test.cpp:83:7:83:8 | u2 |
|
||||
| test.cpp:86:8:86:9 | i1 | test.cpp:83:7:83:8 | u2 |
|
||||
| test.cpp:90:8:90:14 | source1 | test.cpp:89:28:89:34 | source1 |
|
||||
| test.cpp:92:8:92:14 | source1 | test.cpp:89:28:89:34 | source1 |
|
||||
| test.cpp:110:10:110:12 | (reference dereference) | test.cpp:109:9:109:14 | call to source |
|
||||
|
||||
@@ -22,6 +22,5 @@
|
||||
| taint.cpp:250:8:250:8 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:256:8:256:8 | taint.cpp:223:10:223:15 | AST only |
|
||||
| taint.cpp:261:7:261:7 | taint.cpp:258:7:258:12 | AST only |
|
||||
| taint.cpp:350:7:350:7 | taint.cpp:330:6:330:11 | AST only |
|
||||
| taint.cpp:351:7:351:7 | taint.cpp:330:6:330:11 | AST only |
|
||||
| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only |
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
| taint.cpp:290:7:290:7 | x | taint.cpp:275:6:275:11 | call to source |
|
||||
| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
|
||||
| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
|
||||
| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
multipleOperandMemoryLocations
|
||||
missingVirtualVariableForMemoryLocation
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
multipleOperandMemoryLocations
|
||||
missingVirtualVariableForMemoryLocation
|
||||
|
||||
@@ -760,7 +760,7 @@ ssa.cpp:
|
||||
# 179| r179_6(int *) = Load : &:r179_4, m179_5
|
||||
# 179| m179_7(unknown) = InitializeIndirection[p] : &:r179_6
|
||||
# 179| m179_8(unknown) = Chi : total:m179_2, partial:m179_7
|
||||
# 180| m180_1(unknown) = InlineAsm : ~mu179_3
|
||||
# 180| m180_1(unknown) = InlineAsm : ~m179_8
|
||||
# 180| m180_2(unknown) = Chi : total:m179_8, partial:m180_1
|
||||
# 181| r181_1(glval<int>) = VariableAddress[#return] :
|
||||
# 181| r181_2(glval<int *>) = VariableAddress[p] :
|
||||
@@ -811,7 +811,7 @@ ssa.cpp:
|
||||
# 190| r190_4(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_20
|
||||
# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23
|
||||
# 186| m186_1(unknown) = InlineAsm : ~mu184_3, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_1(unknown) = InlineAsm : ~m184_23, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_2(unknown) = Chi : total:m184_23, partial:m186_1
|
||||
# 192| v192_1(void) = NoOp :
|
||||
# 184| v184_24(void) = ReturnIndirection : &:r184_6, ~m186_2
|
||||
@@ -901,7 +901,7 @@ ssa.cpp:
|
||||
# 209| r209_7(void *) = Convert : r209_6
|
||||
# 209| r209_8(int) = Constant[4] :
|
||||
# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8
|
||||
# 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~mu207_3
|
||||
# 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_5
|
||||
# 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8
|
||||
# 209| m209_12(unknown) = Chi : total:m208_3, partial:m209_11
|
||||
# 210| r210_1(glval<int>) = VariableAddress[#return] :
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
multipleOperandMemoryLocations
|
||||
missingVirtualVariableForMemoryLocation
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
multipleOperandMemoryLocations
|
||||
missingVirtualVariableForMemoryLocation
|
||||
|
||||
@@ -92,3 +92,37 @@ void use_printf(float f, double d)
|
||||
size_t three_chars(unsigned char a, unsigned char b, unsigned char c) {
|
||||
return a * b * c; // at most 16581375
|
||||
}
|
||||
|
||||
void g(unsigned char uchar1, unsigned char uchar2, unsigned char uchar3, int i) {
|
||||
unsigned long ulong1, ulong2, ulong3, ulong4, ulong5;
|
||||
ulong1 = (uchar1 + 1) * (uchar2 + 1); // GOOD
|
||||
ulong2 = (i + 1) * (uchar2 + 1); // BAD
|
||||
ulong3 = (uchar1 + 1) * (uchar2 + 1) * (uchar3 + 1); // GOOD
|
||||
|
||||
ulong4 = (uchar1 + (uchar1 + 1)) * (uchar2 + 1); // GOOD
|
||||
ulong5 = (i + (uchar1 + 1)) * (uchar2 + 1); // BAD
|
||||
|
||||
ulong5 = (uchar1 + 1073741824) * uchar2; // BAD [NOT DETECTED]
|
||||
ulong5 = (uchar1 + (1 << 30)) * uchar2; // BAD [NOT DETECTED]
|
||||
ulong5 = uchar1 * uchar1 * uchar1 * uchar2 * uchar2 * uchar2; // BAD [NOT DETECTED]
|
||||
ulong5 = (uchar1 + (unsigned short)(-1)) * (uchar2 + (unsigned short)(-1)); // BAD
|
||||
}
|
||||
|
||||
struct A {
|
||||
short s;
|
||||
int i;
|
||||
};
|
||||
|
||||
void g2(struct A* a, short n) {
|
||||
unsigned long ulong1, ulong2;
|
||||
ulong1 = (a->s - 1) * ((*a).s + 1); // GOOD
|
||||
ulong2 = a->i * (*a).i; // BAD
|
||||
}
|
||||
|
||||
int global_i;
|
||||
unsigned char global_uchar;
|
||||
void g3() {
|
||||
unsigned long ulong1, ulong2;
|
||||
ulong1 = global_i * global_i; // BAD
|
||||
ulong2 = (global_uchar + 1) * 2; // GOOD
|
||||
}
|
||||
@@ -7,3 +7,8 @@
|
||||
| IntMultToLong.c:61:23:61:33 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
|
||||
| IntMultToLong.c:63:23:63:40 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
|
||||
| IntMultToLong.c:75:9:75:13 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. |
|
||||
| IntMultToLong.c:99:14:99:35 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
|
||||
| IntMultToLong.c:103:14:103:46 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
|
||||
| IntMultToLong.c:108:14:108:78 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
|
||||
| IntMultToLong.c:119:14:119:26 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
|
||||
| IntMultToLong.c:126:14:126:32 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
|
||||
|
||||
@@ -13,3 +13,9 @@
|
||||
| test.cpp:82:7:82:11 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:84:7:84:11 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:92:17:92:22 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:113:6:113:10 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:134:19:134:23 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:138:21:138:25 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:144:32:144:36 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:150:32:150:36 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
| test.cpp:153:46:153:50 | ... = ... | Use of '=' where '==' may have been intended. |
|
||||
|
||||
@@ -98,3 +98,58 @@ void g(int *i_p, int cond) {
|
||||
if (y = 1) { // GOOD: y might not be initialized so it is probably intentional.
|
||||
}
|
||||
}
|
||||
|
||||
template<typename>
|
||||
void h() {
|
||||
int x;
|
||||
if(x = 0) { // GOOD: x is not initialized so this is probably intentional
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
if((y = 1)) { // GOOD
|
||||
}
|
||||
|
||||
int z = 0;
|
||||
if(z = 1) { // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void f() {
|
||||
h<int>();
|
||||
}
|
||||
|
||||
void f2() {
|
||||
const char* sz = "abc";
|
||||
|
||||
if(sz = "def") { // GOOD: a == comparison with a string literal is probably not the intent here
|
||||
}
|
||||
}
|
||||
|
||||
void f3(int x, int y) {
|
||||
if(x == 1 && (y = 2)) { // GOOD: the programmer seems to be okay with unparenthesized
|
||||
// comparison operands, so the parenthesis was used to mark this
|
||||
// as an assignment
|
||||
}
|
||||
|
||||
if((x == 1) && (y = 2)) { // BAD
|
||||
}
|
||||
|
||||
long z = x;
|
||||
if(((z == 42) || (y = 2)) && (x == 1)) { // BAD
|
||||
}
|
||||
|
||||
if((y = 2) && (x == z || x == 1)) { // GOOD
|
||||
}
|
||||
|
||||
if(((x == 42) || x == 1) && (y = 2)) { // BAD
|
||||
}
|
||||
|
||||
if(x == 10 || (x == 42 && x == 1) && (y = 2)) { // GOOD
|
||||
}
|
||||
|
||||
if(x == 10 || ((x == 42) && (y = 2)) && (z == 1)) { // BAD
|
||||
}
|
||||
|
||||
if((x == 10) || ((z == z) && (x == 1)) && (y = 2)) { // BAD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,18 @@ struct TemplatedAssignmentBad {
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class Obj3 {
|
||||
public:
|
||||
Obj3<T> &subFunc(const Obj3<T> &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Obj3<T> &operator=(const Obj3<T> &other) {
|
||||
return subFunc(other); // GOOD (returns *this)
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Container c;
|
||||
c = c;
|
||||
@@ -211,5 +223,7 @@ int main() {
|
||||
taGood = 3;
|
||||
TemplatedAssignmentBad taBad;
|
||||
taBad = 4;
|
||||
Obj3<int> obj3_a, obj3_b;
|
||||
obj3_a = obj3_b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
| AV Rule 82.cpp:18:9:18:17 | operator= | Assignment operator in class Bad1 does not return a reference to *this. |
|
||||
| AV Rule 82.cpp:24:8:24:16 | operator= | Assignment operator in class Bad2 should have return type Bad2&. Otherwise a copy is created at each call. |
|
||||
| AV Rule 82.cpp:63:29:63:29 | operator= | Assignment operator in class TemplateReturnAssignment<int> does not return a reference to *this. |
|
||||
| AV Rule 82.cpp:63:29:63:37 | operator= | Assignment operator in class TemplateReturnAssignment<T> does not return a reference to *this. |
|
||||
| AV Rule 82.cpp:199:52:199:52 | operator= | Assignment operator in class TemplatedAssignmentBad should have return type TemplatedAssignmentBad&. Otherwise a copy is created at each call. |
|
||||
|
||||
@@ -94,3 +94,8 @@ void f13_func(int x)
|
||||
{
|
||||
if (x < 10) return; // GOOD
|
||||
}
|
||||
|
||||
int f14()
|
||||
{
|
||||
__asm__("rdtsc"); // GOOD
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user