mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge remote-tracking branch 'upstream/next' into qlucie/master
This commit is contained in:
@@ -9,9 +9,7 @@
|
||||
*/
|
||||
import cpp
|
||||
|
||||
// This query is the JSF version
|
||||
//
|
||||
// (see also InitialisationNotRun.ql and GlobalUseBeforeInit.ql)
|
||||
// See also InitialisationNotRun.ql and GlobalUseBeforeInit.ql
|
||||
|
||||
// Holds if s defines variable v (conservative)
|
||||
predicate defines(ControlFlowNode s, Variable lv) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @name Incorrect Not Operator Usage
|
||||
* @name Incorrect 'not' operator usage
|
||||
* @description Usage of a logical-not (!) operator as an operand for a bit-wise operation.
|
||||
* This commonly indicates the usage of an incorrect operator instead of the bit-wise not (~) operator,
|
||||
* also known as ones' complement operator.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Semmle C/C++ Default Queries
|
||||
Bundle-SymbolicName: com.semmle.plugin.semmlecode.cpp.queries;singleton:=true
|
||||
Bundle-Version: 1.18.3.qualifier
|
||||
Bundle-Vendor: Semmle Ltd.
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.3.qualifier,1.18.3.qualifier]"
|
||||
@@ -68,7 +68,7 @@ class NullAppNameCreateProcessFunctionConfiguration extends DataFlow::Configurat
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
nullValue(source.asExpr())
|
||||
source.asExpr() instanceof NullValue
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
|
||||
@@ -33,14 +33,7 @@ abstract class SystemData extends Element {
|
||||
result = getAnExpr() or
|
||||
|
||||
// flow via global or member variable (conservative approximation)
|
||||
exists(Variable var |
|
||||
(
|
||||
var.getAnAssignedValue() = getAnExprIndirect() or
|
||||
var.getAnAccess() = getAnExprIndirect()
|
||||
) and
|
||||
result = var.getAnAccess() and
|
||||
not var instanceof LocalScopeVariable
|
||||
) or
|
||||
result = getAnAffectedVar().getAnAccess() or
|
||||
|
||||
// flow via stack variable
|
||||
definitionUsePair(_, getAnExprIndirect(), result) or
|
||||
@@ -50,6 +43,17 @@ abstract class SystemData extends Element {
|
||||
// flow from assigned value to assignment expression
|
||||
result.(AssignExpr).getRValue() = getAnExprIndirect()
|
||||
}
|
||||
|
||||
/** Gets a global or member variable that may be affected by this system
|
||||
* data (conservative approximation).
|
||||
*/
|
||||
private Variable getAnAffectedVar() {
|
||||
(
|
||||
result.getAnAssignedValue() = this.getAnExprIndirect() or
|
||||
result.getAnAccess() = this.getAnExprIndirect()
|
||||
) and
|
||||
not result instanceof LocalScopeVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,11 +9,21 @@
|
||||
|
||||
import cpp
|
||||
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
module ASTDataFlow {
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
}
|
||||
|
||||
module IRDataFlow {
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow4
|
||||
}
|
||||
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
from File f, string tag
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds calls to the standard library functions <code>abort, exit, getenv</code> and <code>system</code>.
|
||||
This query highlights calls to the standard library functions <code>abort, exit, getenv</code> and <code>system</code>.
|
||||
The functions <code>abort</code> and <code>exit</code> should not be called as they immediately terminate the program
|
||||
and will bypass all the normal error and exception handling routines in the software. This is especially important in
|
||||
software which is run on systems without an interactive OS, as restarting the software may require a complete reboot
|
||||
|
||||
@@ -195,28 +195,31 @@ predicate freedInSameMethod(Resource r, Expr acquire) {
|
||||
*/
|
||||
predicate leakedInSameMethod(Resource r, Expr acquire) {
|
||||
unreleasedResource(r, acquire, _, _) and
|
||||
(
|
||||
exists(FunctionCall fc |
|
||||
// `r` (or something computed from it) is passed to another function
|
||||
// near to where it's acquired, and might be stored elsewhere.
|
||||
fc.getAnArgument().getAChild*() = r.getAnAccess() and
|
||||
fc.getEnclosingFunction() = acquire.getEnclosingFunction()
|
||||
) or exists(Variable v, Expr e |
|
||||
// `r` (or something computed from it) is stored in another variable
|
||||
// near to where it's acquired, and might be released through that
|
||||
// variable.
|
||||
v.getAnAssignedValue() = e and
|
||||
e.getAChild*() = r.getAnAccess() and
|
||||
e.getEnclosingFunction() = acquire.getEnclosingFunction()
|
||||
) or exists(FunctionCall fc |
|
||||
// `this` (i.e. the class where `r` is acquired) is passed into `r` via a
|
||||
// method, or the constructor. `r` may use this to register itself with
|
||||
// `this` in some way, ensuring it is later deleted.
|
||||
fc.getEnclosingFunction() = acquire.getEnclosingFunction() and
|
||||
fc.getAnArgument() instanceof ThisExpr and
|
||||
(
|
||||
fc.getQualifier() = r.getAnAccess() or // e.g. `r->setOwner(this)`
|
||||
fc = acquire.getAChild*() // e.g. `r = new MyClass(this)`
|
||||
exists(Function f |
|
||||
acquire.getEnclosingFunction() = f and
|
||||
(
|
||||
exists(FunctionCall fc |
|
||||
// `r` (or something computed from it) is passed to another function
|
||||
// near to where it's acquired, and might be stored elsewhere.
|
||||
fc.getAnArgument().getAChild*() = r.getAnAccess() and
|
||||
fc.getEnclosingFunction() = f
|
||||
) or exists(Variable v, Expr e |
|
||||
// `r` (or something computed from it) is stored in another variable
|
||||
// near to where it's acquired, and might be released through that
|
||||
// variable.
|
||||
v.getAnAssignedValue() = e and
|
||||
e.getAChild*() = r.getAnAccess() and
|
||||
e.getEnclosingFunction() = f
|
||||
) or exists(FunctionCall fc |
|
||||
// `this` (i.e. the class where `r` is acquired) is passed into `r` via a
|
||||
// method, or the constructor. `r` may use this to register itself with
|
||||
// `this` in some way, ensuring it is later deleted.
|
||||
fc.getEnclosingFunction() = f and
|
||||
fc.getAnArgument() instanceof ThisExpr and
|
||||
(
|
||||
fc.getQualifier() = r.getAnAccess() or // e.g. `r->setOwner(this)`
|
||||
fc = acquire.getAChild*() // e.g. `r = new MyClass(this)`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -51,7 +51,6 @@ predicate dereferenceThis(Expr e) {
|
||||
* This includes functions whose body is not in the database.
|
||||
*/
|
||||
predicate returnsPointerThis(Function f) {
|
||||
f.getType().getUnspecifiedType() instanceof PointerType and
|
||||
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
|
||||
// `return this`
|
||||
pointerThis(s.getExpr())
|
||||
@@ -64,7 +63,6 @@ predicate returnsPointerThis(Function f) {
|
||||
* database.
|
||||
*/
|
||||
predicate returnsDereferenceThis(Function f) {
|
||||
f.getType().getUnspecifiedType() instanceof ReferenceType and
|
||||
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
|
||||
// `return *this`
|
||||
dereferenceThis(s.getExpr())
|
||||
@@ -76,7 +74,7 @@ predicate assignOperatorWithWrongType(Operator op, string msg) {
|
||||
and exists(op.getBlock())
|
||||
and exists(Class c |
|
||||
c = op.getDeclaringType()
|
||||
and op.getType() = c
|
||||
and op.getType().getUnspecifiedType() = c
|
||||
and msg = "Assignment operator in class " + c.getName() + " should have return type " + c.getName() + "&. Otherwise a copy is created at each call."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule ensures that all operators with opposites (e.g. == and !=) are both defined, and
|
||||
This query ensures that all operators with opposites (e.g. == and !=) are both defined, and
|
||||
that one of them is defined in terms of the other. This just enforces the consistency of meaning
|
||||
of the operators.
|
||||
</p>
|
||||
|
||||
@@ -21,23 +21,50 @@ predicate oppositeOperators(string op1, string op2) {
|
||||
/* this match is very syntactic: we simply check that op1 is defined as
|
||||
!op2(_, _) */
|
||||
predicate implementedAsNegationOf(Operator op1, Operator op2) {
|
||||
exists(Block b, ReturnStmt r, NotExpr n, FunctionCall c |
|
||||
exists(Block b, ReturnStmt r, NotExpr n, Expr o |
|
||||
b = op1.getBlock() and
|
||||
b.getNumStmt() = 1 and
|
||||
r = b.getStmt(0) and
|
||||
n = r.getExpr() and
|
||||
c = n.getOperand() and
|
||||
c.getTarget() = op2)
|
||||
o = n.getOperand() and
|
||||
(
|
||||
o instanceof LTExpr and op2.hasName("operator<") or
|
||||
o instanceof LEExpr and op2.hasName("operator<=") or
|
||||
o instanceof GTExpr and op2.hasName("operator>") or
|
||||
o instanceof GEExpr and op2.hasName("operator>=") or
|
||||
o instanceof EQExpr and op2.hasName("operator==") or
|
||||
o instanceof NEExpr and op2.hasName("operator!=") or
|
||||
o.(FunctionCall).getTarget() = op2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate classIsCheckableFor(Class c, string op) {
|
||||
oppositeOperators(op, _) and
|
||||
// We check the template, not its instantiations
|
||||
not c instanceof ClassTemplateInstantiation and
|
||||
// Member functions of templates are not necessarily instantiated, so
|
||||
// if the function we want to check exists, then make sure that its
|
||||
// body also exists
|
||||
((c instanceof TemplateClass)
|
||||
implies
|
||||
forall(Function f | f = c.getAMember() and f.hasName(op)
|
||||
| exists(f.getEntryPoint())))
|
||||
}
|
||||
|
||||
from Class c, string op, string opp, Operator rator
|
||||
where c.fromSource() and
|
||||
oppositeOperators(op, opp) and
|
||||
classIsCheckableFor(c, op) and
|
||||
classIsCheckableFor(c, opp) and
|
||||
rator = c.getAMember() and
|
||||
rator.hasName(op) and
|
||||
not exists(Operator oprator | oprator = c.getAMember() and
|
||||
oprator.hasName(opp) and
|
||||
( implementedAsNegationOf(rator, oprator)
|
||||
or implementedAsNegationOf(oprator, rator)))
|
||||
forex(Operator aRator |
|
||||
aRator = c.getAMember() and aRator.hasName(op) |
|
||||
not exists(Operator oprator |
|
||||
oprator = c.getAMember() and
|
||||
oprator.hasName(opp) and
|
||||
( implementedAsNegationOf(aRator, oprator)
|
||||
or implementedAsNegationOf(oprator, aRator))))
|
||||
select c, "When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator " + op +
|
||||
" is declared on line " + rator.getLocation().getStartLine().toString() + ", but it is not defined in terms of its opposite operator " + opp + "."
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds return statements that return pointers to an object allocated on the stack. The lifetime
|
||||
This query highlights return statements that return pointers to an object allocated on the stack. The lifetime
|
||||
of a stack allocated memory location only lasts until the function returns, , and
|
||||
the contents of that memory become undefined after that. Clearly, using a pointer to stack
|
||||
memory after the function has already returned will have undefined results.
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.
|
||||
This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.
|
||||
This should be avoided as it can cause confusion about the actual variable being used in an expression.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds variables with the <code>register</code> storage class specifier. Modern compilers are now capable of
|
||||
This query highlights variables with the <code>register</code> storage class specifier. Modern compilers are now capable of
|
||||
optimal register placement, and overriding it could lead to worse performance.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds portions of code that can expose the floating point implementation of the underlying
|
||||
This query highlights portions of code that can expose the floating point implementation of the underlying
|
||||
machine. Manually manipulating the bits in the float is prone to mistakes and is unportable. Floating point
|
||||
implementations can vary across architectures, and bit-field packing can differ across compilers,
|
||||
making manual bit-manipulation of floats inadvisable.
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds string literals that are assigned to a non-<code>const</code> variable. String literals
|
||||
This query highlights string literals that are assigned to a non-<code>const</code> variable. String literals
|
||||
should not be changed, since they are usually stored in the data section, and depending on the architecture,
|
||||
writing to the data section will cause undefined behavior, such as memory corruption or memory write error.
|
||||
</p>
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This query finds bit fields with members that are not explicitly declared to be unsigned.
|
||||
The sign of plain char, short, int, or long bit field is implementation-specific, and declaring
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>
|
||||
This rule finds unsigned values that are being negated. Behavior is undefined in such cases.
|
||||
This query finds unsigned values that are being negated. Behavior is undefined in such cases.
|
||||
Negating integer values produces the two's complement of that number, which cannot represent negative
|
||||
values of large unsigned values (values where the sign bit is used) and are most likely to be interpreted
|
||||
as a smaller positive integer instead.
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
|
||||
<include src="cpp/jsfNote.qhelp" />
|
||||
|
||||
<p>Use of goto statements makes code more difficult to understand and maintain. Consequently, the use
|
||||
of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops.
|
||||
This rule identifies any goto statements that are called directly or from a single nested loop as violations.</p>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="semmlecode-cpp-queries"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.library"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.dbscheme"/>
|
||||
<path value="/semmlecode.cpp.dbscheme"/>
|
||||
</extension>
|
||||
</plugin>
|
||||
@@ -418,6 +418,12 @@ class Class extends UserType {
|
||||
*/
|
||||
predicate isPOD() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this class is a standard-layout class [N4140 9(7)]. Also holds
|
||||
* for structs in C programs.
|
||||
*/
|
||||
predicate isStandardLayout() { is_standard_layout_class(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this class is abstract, in other words whether it declares one
|
||||
* or more pure virtual member functions.
|
||||
|
||||
@@ -542,6 +542,42 @@ class FunctionNode extends ASTNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an `ClassAggregateLiteral`.
|
||||
*/
|
||||
class ClassAggregateLiteralNode extends ExprNode {
|
||||
ClassAggregateLiteral list;
|
||||
|
||||
ClassAggregateLiteralNode() {
|
||||
list = ast
|
||||
}
|
||||
|
||||
override string getChildEdgeLabel(int childIndex) {
|
||||
exists(Field field |
|
||||
list.getFieldExpr(field) = list.getChild(childIndex) and
|
||||
result = "." + field.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an `ArrayAggregateLiteral`.
|
||||
*/
|
||||
class ArrayAggregateLiteralNode extends ExprNode {
|
||||
ArrayAggregateLiteral list;
|
||||
|
||||
ArrayAggregateLiteralNode() {
|
||||
list = ast
|
||||
}
|
||||
|
||||
override string getChildEdgeLabel(int childIndex) {
|
||||
exists(int elementIndex |
|
||||
list.getElementExpr(elementIndex) = list.getChild(childIndex) and
|
||||
result = "[" + elementIndex.toString() + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate nodes(PrintASTNode node, string key, string value) {
|
||||
node.shouldPrint() and
|
||||
value = node.getProperty(key)
|
||||
|
||||
@@ -77,6 +77,23 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
// The base case of `reachable` is factored out for performance. If it's
|
||||
// written in-line in `reachable`, the compiler inserts a `n instanceof
|
||||
// ControlFlowNode` check because the `not ... and not ...` case doesn't
|
||||
// otherwise bind `n`. The problem is that this check is inserted at the
|
||||
// outermost level of this predicate, so it covers all cases including the
|
||||
// recursive case. The optimizer doesn't eliminate the check even though it's
|
||||
// redundant, and having the check leads to needless extra computation and a
|
||||
// risk of bad join orders.
|
||||
private predicate reachableBaseCase(ControlFlowNode n) {
|
||||
exists(Function f | f.getEntryPoint() = n)
|
||||
or
|
||||
// Okay to use successors_extended directly here
|
||||
(not successors_extended(_,n) and not successors_extended(n,_))
|
||||
or
|
||||
n instanceof Handler
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the control-flow node `n` is reachable, meaning that either
|
||||
* it is an entry point, or there exists a path in the control-flow
|
||||
@@ -88,14 +105,9 @@ private cached module Cached {
|
||||
cached
|
||||
predicate reachable(ControlFlowNode n)
|
||||
{
|
||||
exists(Function f | f.getEntryPoint() = n)
|
||||
or
|
||||
// Okay to use successors_extended directly here
|
||||
(not successors_extended(_,n) and not successors_extended(n,_))
|
||||
reachableBaseCase(n)
|
||||
or
|
||||
reachable(n.getAPredecessor())
|
||||
or
|
||||
n instanceof Handler
|
||||
}
|
||||
|
||||
/** Holds if `condition` always evaluates to a nonzero value. */
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Holds if `block` consists of an `UnreachedInstruction`.
|
||||
*
|
||||
* We avoiding reporting an unreached block as being controlled by a guard. The unreached block
|
||||
* has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock`
|
||||
* and the `IRBlock`.
|
||||
*/
|
||||
private predicate isUnreachedBlock(IRBlock block) {
|
||||
block.getFirstInstruction() instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
||||
* operands of logical operators but not switch statements.
|
||||
@@ -215,7 +226,8 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
||||
exists(IRBlock irb |
|
||||
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
|
||||
irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled
|
||||
irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -301,6 +313,7 @@ class IRGuardCondition extends Instruction {
|
||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||
*/
|
||||
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
|
||||
not isUnreachedBlock(controlled) and
|
||||
exists(IRBlock thisblock
|
||||
| thisblock.getAnInstruction() = this
|
||||
| exists(IRBlock succ, ConditionalBranchInstruction branch
|
||||
|
||||
@@ -96,10 +96,18 @@ abstract class LocalScopeVariableReachability extends string {
|
||||
|
||||
private predicate bbEntryReachesLocally(BasicBlock bb, SemanticStackVariable v, ControlFlowNode node) {
|
||||
exists(int n |
|
||||
node = bb.getNode(n) and isSink(node, v) |
|
||||
not exists(int m | m < n | isBarrier(bb.getNode(m), v))
|
||||
node = bb.getNode(n) and
|
||||
isSink(node, v)
|
||||
|
|
||||
not exists(this.firstBarrierIndexIn(bb, v))
|
||||
or
|
||||
n <= this.firstBarrierIndexIn(bb, v)
|
||||
)
|
||||
}
|
||||
|
||||
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
|
||||
result = min(int m | isBarrier(bb.getNode(m), v))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
/**
|
||||
* DEPRECATED: Recursion through `DataFlow::Configuration` is impossible in
|
||||
* Semmle Core 1.17 and above. There is no need for this module because it's
|
||||
* impossible to accidentally depend on recursion through
|
||||
* `DataFlow::Configuration` in current releases.
|
||||
*
|
||||
* When this module is imported, recursive use of `DataFlow::Configuration` is
|
||||
* disallowed. Importing this module will guarantee the absence of such
|
||||
* recursion, which is unsupported and will be unconditionally disallowed in a
|
||||
|
||||
@@ -292,10 +292,7 @@ module FlowVar_internal {
|
||||
* Gets a variable that is assigned in this loop and read outside the loop.
|
||||
*/
|
||||
private Variable getARelevantVariable() {
|
||||
exists(BasicBlock bbAssign |
|
||||
assignmentLikeOperation(bbAssign.getANode(), result, _) and
|
||||
this.bbInLoop(bbAssign)
|
||||
) and
|
||||
result = this.getAVariableAssignedInLoop() and
|
||||
exists(VariableAccess va |
|
||||
va.getTarget() = result and
|
||||
readAccess(va) and
|
||||
@@ -303,6 +300,15 @@ module FlowVar_internal {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a variable that is assigned in this loop. */
|
||||
pragma[noinline]
|
||||
private Variable getAVariableAssignedInLoop() {
|
||||
exists(BasicBlock bbAssign |
|
||||
assignmentLikeOperation(bbAssign.getANode(), result, _) and
|
||||
this.bbInLoop(bbAssign)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbInLoopCondition(BasicBlock bb) {
|
||||
getCFNParent*(bb.getANode()) = this.(Loop).getCondition()
|
||||
}
|
||||
|
||||
@@ -202,6 +202,27 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu
|
||||
override string toString() { result = "__builtin_shufflevector" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A clang `__builtin_convertvector` expression.
|
||||
*/
|
||||
class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector {
|
||||
override string toString() { result = "__builtin_convertvector" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A clang `__builtin_addressof` expression (can be used to implement C++'s std::addressof).
|
||||
*/
|
||||
class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, @builtinaddressof {
|
||||
/** Gets the function or variable whose address is taken. */
|
||||
Declaration getAddressable() {
|
||||
result = this.getOperand().(Access).getTarget()
|
||||
// this handles the case where we are taking the address of a reference variable
|
||||
or result = this.getOperand().(ReferenceDereferenceExpr).getChild(0).(Access).getTarget()
|
||||
}
|
||||
|
||||
override string getOperator() { result = "__builtin_addressof" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_constructible` type trait.
|
||||
*/
|
||||
@@ -369,3 +390,10 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
|
||||
class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
|
||||
override string toString() { result = "__builtin_choose_expr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill operation on a GNU vector.
|
||||
*/
|
||||
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
||||
override string getOperator() { result = "(vector fill)" }
|
||||
}
|
||||
|
||||
@@ -497,7 +497,12 @@ class DynamicCast extends Cast, @dynamic_cast {
|
||||
* specified by the `__declspec(uuid)` attribute.
|
||||
*/
|
||||
class UuidofOperator extends Expr, @uuidof {
|
||||
override string toString() { result = "__uuidof(" + getTypeOperand().getName() + ")" }
|
||||
override string toString() {
|
||||
if exists(getTypeOperand()) then
|
||||
result = "__uuidof(" + getTypeOperand().getName() + ")"
|
||||
else
|
||||
result = "__uuidof(0)"
|
||||
}
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
|
||||
|
||||
@@ -67,13 +67,28 @@ class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr {
|
||||
*/
|
||||
class ConditionalExpr extends Operation, @conditionalexpr {
|
||||
/** Gets the condition of this conditional expression. */
|
||||
Expr getCondition() { this.hasChild(result,0) }
|
||||
Expr getCondition() {
|
||||
expr_cond_guard(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/** Gets the 'then' expression of this conditional expression. */
|
||||
Expr getThen() { this.hasChild(result,1) }
|
||||
Expr getThen() {
|
||||
if this.isTwoOperand()
|
||||
then result = this.getCondition()
|
||||
else expr_cond_true(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/** Gets the 'else' expression of this conditional expression. */
|
||||
Expr getElse() { this.hasChild(result,2) }
|
||||
Expr getElse() {
|
||||
expr_cond_false(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression used the two operand form `guard ? : false`.
|
||||
*/
|
||||
predicate isTwoOperand() {
|
||||
expr_cond_two_operand(underlyingElement(this))
|
||||
}
|
||||
|
||||
override string getOperator() { result = "?" }
|
||||
|
||||
|
||||
23
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll
Normal file
23
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis: deciding whether data can flow from a _source_ to a
|
||||
* _sink_. This library differs from the one in `semmle.code.cpp.dataflow` in that
|
||||
* this library uses the IR (Intermediate Representation) library, which provides
|
||||
* a more precise semantic representation of the program, whereas the other dataflow
|
||||
* library uses the more syntax-oriented ASTs. This library should provide more accurate
|
||||
* results than the AST-based library in most scenarios.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
|
||||
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
import cpp
|
||||
|
||||
module DataFlow {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
import cpp
|
||||
|
||||
module DataFlow2 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow3.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow3.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
import cpp
|
||||
|
||||
module DataFlow3 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow4.qll
Normal file
38
cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow4.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use
|
||||
* this class when data-flow configurations must depend on each other. Two
|
||||
* classes extending `DataFlow::Configuration` should never depend on each
|
||||
* other, but one of them should instead depend on a
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
|
||||
*/
|
||||
import cpp
|
||||
|
||||
module DataFlow4 {
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
private import cpp
|
||||
private import DataFlowPrivate
|
||||
|
||||
Function viableImpl(MethodAccess ma) {
|
||||
result = ma.getTarget()
|
||||
}
|
||||
|
||||
Function viableCallable(Call call) {
|
||||
result = call.getTarget()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable dispatch
|
||||
* targets of `ma` in `c`.
|
||||
*/
|
||||
predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which a context might make a difference.
|
||||
*/
|
||||
private Method viableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
// stub implementation
|
||||
result = viableImpl(ma) and
|
||||
viableCallable(ctx) = ma.getEnclosingFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which the context makes a difference.
|
||||
*/
|
||||
Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInCallContext(ma, _, ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data might flow from `ma` to a return statement in some
|
||||
* configuration.
|
||||
*/
|
||||
private predicate maybeChainedReturn(MethodAccess ma) {
|
||||
exists(ReturnStmt ret |
|
||||
exists(ret.getExpr()) and
|
||||
ret.getEnclosingFunction() = ma.getEnclosingFunction() and
|
||||
not ma.getParent() instanceof ExprStmt
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from `m` to `ma` might return further and if
|
||||
* this path restricts the set of call sites that can be returned to.
|
||||
*/
|
||||
predicate reducedViableImplInReturn(Method m, MethodAccess ma) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
m = viableImpl(ma) and
|
||||
ctxtgts = count(Call ctx | m = viableImplInCallContext(ma, ctx)) and
|
||||
tgts = strictcount(Call ctx | viableCallable(ctx) = ma.getEnclosingFunction()) and
|
||||
ctxtgts < tgts
|
||||
) and
|
||||
maybeChainedReturn(ma)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s and results for which the return flow from the
|
||||
* result to `ma` restricts the possible context `ctx`.
|
||||
*/
|
||||
Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInReturn(result, ma)
|
||||
}
|
||||
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
Normal file
File diff suppressed because it is too large
Load Diff
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
Normal file
1614
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
|
||||
cached
|
||||
private module ImplCommon {
|
||||
/**
|
||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
||||
* The instance parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParam(Call call, int i, ParameterNode p) {
|
||||
exists(Callable callable |
|
||||
callable = viableCallable(call) and
|
||||
p.isParameterOf(callable, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i, Call call |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node) {
|
||||
p = node
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a `ReturnNode` in the same callable using only
|
||||
* value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsThrough(ParameterNode p) {
|
||||
exists(ReturnNode ret | parameterValueFlow(p, ret))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows through `call` using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ExprNode call) {
|
||||
exists(ParameterNode param |
|
||||
viableParamArg(param, arg) and
|
||||
parameterValueFlowsThrough(param) and
|
||||
arg.argumentOf(call.getExpr(), _) and
|
||||
compatibleTypes(arg.getType(), call.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to the pre-update node of `n` in the same callable
|
||||
* using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a value-preserving method.
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2)
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculation of `predicate store(Node node1, Content f, Node node2)`:
|
||||
* There are three cases:
|
||||
* - The base case: A direct local assignment given by `storeStep`.
|
||||
* - A call to a method or constructor with two arguments, `arg1` and `arg2`,
|
||||
* such the call has the side-effect `arg2.f = arg1`.
|
||||
* - A call to a method that returns an object in which an argument has been
|
||||
* stored.
|
||||
* `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
|
||||
* the third case.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
* `f` or via a call that acts as a setter.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, Content f, Node node2) {
|
||||
storeViaSideEffect(node1, f, node2) or
|
||||
storeReturn(node1, f, node2)
|
||||
}
|
||||
|
||||
private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Call call, int i1, int i2 |
|
||||
setterCall(call, i1, i2, f) and
|
||||
node1.(ArgumentNode).argumentOf(call, i1) and
|
||||
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
|
||||
exists(Node n1, PostUpdateNode n2 |
|
||||
parameterValueFlow(p1, n1) and
|
||||
storeViaSideEffect(n1, f, n2) and
|
||||
parameterValueFlow(p2, n2.getPreUpdateNode()) and
|
||||
p1 != p2
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate setterCall(Call call, int i1, int i2, Content f) {
|
||||
exists(Callable callable, ParameterNode p1, ParameterNode p2 |
|
||||
setterInParam(p1, f, p2) and
|
||||
callable = viableCallable(call) and
|
||||
p1.isParameterOf(callable, i1) and
|
||||
p2.isParameterOf(callable, i2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate storeReturn(Node node1, Content f, Node node2) {
|
||||
exists(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct read of `f` or
|
||||
* via a getter.
|
||||
*/
|
||||
cached
|
||||
predicate read(Node node1, Content f, Node node2) {
|
||||
readStep(node1, f, node2) and storeStep(_, f, _)
|
||||
or
|
||||
exists(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate localStoreReadStep(Node node1, Node node2) {
|
||||
exists(Node mid1, Node mid2, Content f |
|
||||
store(node1, f, mid1) and
|
||||
localValueStep*(mid1, mid2) and
|
||||
read(mid2, f, node2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||
* expression that reaches a `this` parameter.
|
||||
*/
|
||||
private predicate callHasInstanceArgument(Call call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(Call call, int i, boolean emptyAp) {
|
||||
reducedViableImplInCallContext(_, _, call) and
|
||||
(emptyAp = true or emptyAp = false) and
|
||||
(
|
||||
exists(call.getArgument(i))
|
||||
or
|
||||
i = -1 and callHasInstanceArgument(call)
|
||||
)
|
||||
} or
|
||||
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
|
||||
TReturn(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) }
|
||||
}
|
||||
import ImplCommon
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and match the
|
||||
* call sites of flow into a method with flow out of a method.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(Call call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Method m, MethodAccess ma)` : Flow reached `ma` from `m` and
|
||||
* this dispatch target of `ma` implies a reduced set of dispatch origins
|
||||
* to which data may flow if it should reach a `return` statement.
|
||||
*/
|
||||
abstract class CallContext extends TCallContext { abstract string toString(); }
|
||||
|
||||
class CallContextAny extends CallContext, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
}
|
||||
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() { result = "CcCall" }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() { result = "CcReturn" }
|
||||
}
|
||||
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, Callable callable, Call call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
or
|
||||
exists(Method m0, MethodAccess ma0 |
|
||||
ma0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(m0, ma0) and
|
||||
m0 = prunedViableImplInCallContextReverse(ma0, call)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[call, cc]
|
||||
Callable resolveCall(Call call, CallContext cc) {
|
||||
exists(Call ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then result = prunedViableImplInCallContext(call, ctx)
|
||||
else result = viableCallable(call)
|
||||
)
|
||||
or
|
||||
result = viableCallable(call) and cc instanceof CallContextSomeCall
|
||||
or
|
||||
result = viableCallable(call) and cc instanceof CallContextAny
|
||||
or
|
||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
private import cpp
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Instance arguments (`this` pointer) are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(CallInstruction call |
|
||||
this = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(Call call, int pos) {
|
||||
exists (CallInstruction callInstr |
|
||||
callInstr.getAST() = call and
|
||||
(
|
||||
this = callInstr.getPositionalArgument(pos) or
|
||||
this = callInstr.getThisArgument() and pos = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node {
|
||||
ReturnNode() {
|
||||
exists(ReturnValueInstruction ret | this = ret.getReturnValue() )
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` does not pass an implicit or explicit qualifier, i.e., a
|
||||
* `this` parameter.
|
||||
*/
|
||||
predicate callHasQualifier(Call call) {
|
||||
call.hasQualifier()
|
||||
or
|
||||
call.getTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TArrayContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
* contents of a collection object, or the contents of an array.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getContainerType();
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
Field f;
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
Field getField() { result = f }
|
||||
override string toString() { result = f.toString() }
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
override RefType getContainerType() { result = f.getDeclaringType() }
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getContainerType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType getContainerType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative (boxed) type for `t` for the purpose of pruning
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
RefType getErasedRepr(Type t) {
|
||||
suppressUnusedType(t) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
result = t.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
any() // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedType(Type t) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RefType extends Type {
|
||||
}
|
||||
|
||||
class CastExpr extends Expr {
|
||||
CastExpr() { none() } // stub implementation
|
||||
}
|
||||
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
Argument() {
|
||||
call.getArgument(pos) = this
|
||||
}
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() {
|
||||
result = pos
|
||||
}
|
||||
}
|
||||
|
||||
class Callable extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `Function` in the C++ library. In the Java library, a `Method`
|
||||
* is any callable except a constructor.
|
||||
*/
|
||||
class Method extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `FunctionCall` in the C++ library. In the Java library, a
|
||||
* `MethodAccess` is any `Call` that does not call a constructor.
|
||||
*/
|
||||
class MethodAccess extends FunctionCall {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getEnclosingFunction`.
|
||||
*/
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.getEnclosingFunction()
|
||||
}
|
||||
}
|
||||
135
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
Normal file
135
cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
*
|
||||
* A node can be either an expression, a parameter, or an uninitialized local
|
||||
* variable. Such nodes are created with `DataFlow::exprNode`,
|
||||
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
|
||||
*/
|
||||
class Node extends Instruction {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
Function getEnclosingCallable() {
|
||||
result = this.getFunction()
|
||||
}
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.getResultType()
|
||||
}
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.getConvertedResultExpression() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(InitializeParameterInstruction).getParameter() }
|
||||
|
||||
/**
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
*/
|
||||
LocalVariable asUninitialized() {
|
||||
result = this.(UninitializedInstruction).getLocalVariable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() { result = getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { expr = this.asExpr() }
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParameterNode extends Node, InitializeParameterInstruction {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
predicate isParameterOf(Function f, int i) {
|
||||
f.getParameter(i) = getParameter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of an uninitialized local variable, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class UninitializedNode extends Node, UninitializedInstruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*
|
||||
* This class exists to match the interface used by Java. There are currently no non-abstract
|
||||
* classes that extend it. When we implement field flow, we can revisit this.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to `e`.
|
||||
*/
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of `p` at function entry.
|
||||
*/
|
||||
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
*/
|
||||
UninitializedNode uninitializedNode(LocalVariable v) {
|
||||
result.getLocalVariable() = v
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
nodeTo.(CopyInstruction).getSourceValue() = nodeFrom or
|
||||
nodeTo.(PhiInstruction).getAnOperand().getDefinitionInstruction() = nodeFrom or
|
||||
// Treat all conversions as flow, even conversions between different numeric types.
|
||||
nodeTo.(ConvertInstruction).getOperand() = nodeFrom
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) {
|
||||
localFlowStep*(source, sink)
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
import cpp
|
||||
|
||||
newtype TMemoryAccessKind =
|
||||
private newtype TMemoryAccessKind =
|
||||
TIndirectMemoryAccess() or
|
||||
TIndirectMayMemoryAccess() or
|
||||
TBufferMemoryAccess() or
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess()
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
TChiPartialMemoryAccess()
|
||||
|
||||
/**
|
||||
* Describes the set of memory locations memory accessed by a memory operand or
|
||||
@@ -15,8 +20,8 @@ class MemoryAccessKind extends TMemoryAccessKind {
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory at the address specified by the
|
||||
* `AddressOperand` on the same instruction.
|
||||
* The operand or result accesses memory at the address specified by the `AddressOperand` on the
|
||||
* same instruction.
|
||||
*/
|
||||
class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
override string toString() {
|
||||
@@ -24,6 +29,38 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory at the address specified by the
|
||||
* `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "indirect(may)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory starting at the address specified by the `AddressOperand`
|
||||
* on the same instruction, accessing a number of consecutive elements given by the
|
||||
* `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
|
||||
override string toString() {
|
||||
result = "buffer"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory starting at the address
|
||||
* specified by the `AddressOperand` on the same instruction, accessing a number of consecutive
|
||||
* elements given by the `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "buffer(may)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses all memory whose address has escaped.
|
||||
*/
|
||||
@@ -43,6 +80,26 @@ class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiTotal operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
|
||||
override string toString() {
|
||||
result = "chi(total)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiPartial operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
|
||||
override string toString() {
|
||||
result = "chi(partial)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand accesses memory not modeled in SSA. Used only on the result of
|
||||
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.
|
||||
|
||||
@@ -54,11 +54,22 @@ private newtype TOpcode =
|
||||
TUnwind() or
|
||||
TUnmodeledDefinition() or
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TPhi() or
|
||||
TVarArgsStart() or
|
||||
TVarArgsEnd() or
|
||||
TVarArg() or
|
||||
TVarArgCopy()
|
||||
TVarArgCopy() or
|
||||
TCallSideEffect() or
|
||||
TCallReadSideEffect() or
|
||||
TIndirectReadSideEffect() or
|
||||
TIndirectWriteSideEffect() or
|
||||
TIndirectMayWriteSideEffect() or
|
||||
TBufferReadSideEffect() or
|
||||
TBufferWriteSideEffect() or
|
||||
TBufferMayWriteSideEffect() or
|
||||
TChi() or
|
||||
TUnreached()
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() {
|
||||
@@ -92,6 +103,29 @@ abstract class OpcodeWithCondition extends Opcode {}
|
||||
|
||||
abstract class BuiltInOpcode extends Opcode {}
|
||||
|
||||
abstract class SideEffectOpcode extends Opcode {}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class ReadSideEffectOpcode extends SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class WriteSideEffectOpcode extends SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* 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 SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends MemoryAccessOpcode {}
|
||||
|
||||
module Opcode {
|
||||
class NoOp extends Opcode, TNoOp { override final string toString() { result = "NoOp" } }
|
||||
class Uninitialized extends MemoryAccessOpcode, TUninitialized { override final string toString() { result = "Uninitialized" } }
|
||||
@@ -148,9 +182,20 @@ module Opcode {
|
||||
class Unwind extends Opcode, TUnwind { override final string toString() { result = "Unwind" } }
|
||||
class UnmodeledDefinition extends Opcode, TUnmodeledDefinition { override final string toString() { result = "UnmodeledDefinition" } }
|
||||
class UnmodeledUse extends Opcode, TUnmodeledUse { override final string toString() { result = "UnmodeledUse" } }
|
||||
class AliasedDefinition extends Opcode, TAliasedDefinition { override final string toString() { result = "AliasedDefinition" } }
|
||||
class Phi extends Opcode, TPhi { override final string toString() { result = "Phi" } }
|
||||
class VarArgsStart extends BuiltInOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } }
|
||||
class VarArgsEnd extends BuiltInOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } }
|
||||
class VarArg extends BuiltInOpcode, TVarArg { override final string toString() { result = "VarArg" } }
|
||||
class VarArgCopy extends BuiltInOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } }
|
||||
class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect { override final string toString() { result = "CallSideEffect" } }
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect { override final string toString() { result = "CallReadSideEffect" } }
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode, TIndirectReadSideEffect { override final string toString() { result = "IndirectReadSideEffect" } }
|
||||
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode, TIndirectWriteSideEffect { override final string toString() { result = "IndirectWriteSideEffect" } }
|
||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode, TIndirectMayWriteSideEffect { override final string toString() { result = "IndirectMayWriteSideEffect" } }
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
|
||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
|
||||
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
|
||||
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
* sequence.
|
||||
*
|
||||
* This class does not contain any members that query the predecessor or successor edges of the
|
||||
* block. This allows different classes that extend `IRBlockBase` to expose different subsets of
|
||||
* edges (e.g. ignoring unreachable edges).
|
||||
*
|
||||
* Most consumers should use the class `IRBlock`.
|
||||
*/
|
||||
class IRBlockBase extends TIRBlock {
|
||||
final string toString() {
|
||||
result = getFirstInstruction(this).toString()
|
||||
}
|
||||
@@ -59,7 +70,14 @@ class IRBlock extends TIRBlock {
|
||||
final Function getFunction() {
|
||||
result = getFirstInstruction(this).getFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block with additional information about its predecessor and successor edges. Each edge
|
||||
* corresponds to the control flow between the last instruction of one block and the first
|
||||
* instruction of another block.
|
||||
*/
|
||||
class IRBlock extends IRBlockBase {
|
||||
final IRBlock getASuccessor() {
|
||||
blockSuccessor(this, result)
|
||||
}
|
||||
@@ -98,3 +116,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
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 MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -95,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*/
|
||||
final string getOperationString() {
|
||||
if exists(getImmediateString()) then
|
||||
result = opcode.toString() + "[" + getImmediateString() + "]"
|
||||
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
|
||||
else
|
||||
result = opcode.toString()
|
||||
result = getOperationPrefix() + opcode.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction {
|
||||
none()
|
||||
}
|
||||
|
||||
private string getOperationPrefix() {
|
||||
if this instanceof SideEffectInstruction then
|
||||
result = "^"
|
||||
else
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if resultType instanceof VoidType then
|
||||
result = "v"
|
||||
@@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class SideEffectInstruction extends Instruction {
|
||||
SideEffectInstruction() {
|
||||
opcode instanceof SideEffectOpcode
|
||||
}
|
||||
|
||||
final Instruction getPrimaryInstruction() {
|
||||
result = Construction::getPrimaryInstructionForSideEffect(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be
|
||||
* accessed by that call.
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be read
|
||||
* by that call.
|
||||
*/
|
||||
class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
CallReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferMayWriteSideEffect
|
||||
}
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes all escaped memory.
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() {
|
||||
opcode instanceof Opcode::AliasedDefinition
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() {
|
||||
opcode instanceof Opcode::UnmodeledUse
|
||||
@@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the choice of one of multiple input values based on control flow.
|
||||
*
|
||||
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
|
||||
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
|
||||
* each control flow predecessor of the block, with that operand representing the version of the
|
||||
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
|
||||
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
|
||||
* runtime.
|
||||
*/
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() {
|
||||
opcode instanceof Opcode::Phi
|
||||
@@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the effect that a write to a memory may have on potential aliases of
|
||||
* that memory.
|
||||
*
|
||||
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
|
||||
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
|
||||
* the previous state of all of the memory that might be alised by the memory write. The second
|
||||
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
|
||||
* memory write. The result of the `ChiInstruction` represents the same memory as
|
||||
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
|
||||
* the memory write.
|
||||
*
|
||||
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
|
||||
* same memory:
|
||||
* ```
|
||||
* *p = 5;
|
||||
* x = *q;
|
||||
* ```
|
||||
*
|
||||
* The IR would look like:
|
||||
* ```
|
||||
* r1_1 = VariableAddress[p]
|
||||
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
|
||||
* r1_3 = Constant[5]
|
||||
* m1_4 = Store r1_2, r1_3 // Store to `*p`
|
||||
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
|
||||
* r1_6 = VariableAddress[x]
|
||||
* r1_7 = VariableAddress[q]
|
||||
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
|
||||
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
|
||||
* m1_10 = Store r1_6, r1_9 // Store to x
|
||||
* ```
|
||||
*
|
||||
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
|
||||
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
|
||||
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
|
||||
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
|
||||
* pointed to by `q`.
|
||||
*
|
||||
* For more information about how `Chi` instructions are used to model memory side effects, see
|
||||
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
|
||||
*/
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() {
|
||||
opcode instanceof Opcode::Chi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
*/
|
||||
final Instruction getTotalOperand() {
|
||||
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the new value written by the memory write.
|
||||
*/
|
||||
final Instruction getPartialOperand() {
|
||||
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
SideEffectOperand() {
|
||||
this = TNonPhiOperand(_, sideEffectOperand(), _)
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
instr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
or
|
||||
instr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,3 +397,38 @@ class MemoryOperand extends Operand {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends Operand {
|
||||
ChiTotalOperand() {
|
||||
this = TNonPhiOperand(_, chiTotalOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends Operand {
|
||||
ChiPartialOperand() {
|
||||
this = TNonPhiOperand(_, chiPartialOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiPartialMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
|
||||
@@ -0,0 +1,280 @@
|
||||
import cpp
|
||||
import AliasAnalysis
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private newtype TVirtualVariable =
|
||||
TVirtualIRVariable(IRVariable var) {
|
||||
not variableAddressEscapes(var)
|
||||
} or
|
||||
TUnknownVirtualVariable(FunctionIR f)
|
||||
|
||||
private VirtualIRVariable getVirtualVariable(IRVariable var) {
|
||||
result.getIRVariable() = var
|
||||
}
|
||||
|
||||
private UnknownVirtualVariable getUnknownVirtualVariable(FunctionIR f) {
|
||||
result.getFunctionIR() = f
|
||||
}
|
||||
|
||||
class VirtualVariable extends TVirtualVariable {
|
||||
string toString() {
|
||||
none()
|
||||
}
|
||||
|
||||
string getUniqueId() {
|
||||
none()
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
VirtualIRVariable() {
|
||||
this = TVirtualIRVariable(var)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = var.toString()
|
||||
}
|
||||
|
||||
final IRVariable getIRVariable() {
|
||||
result = var
|
||||
}
|
||||
|
||||
// REVIEW: This should just be on MemoryAccess
|
||||
override final Type getType() {
|
||||
result = var.getType()
|
||||
}
|
||||
|
||||
override final string getUniqueId() {
|
||||
result = var.getUniqueId()
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable {
|
||||
FunctionIR f;
|
||||
|
||||
UnknownVirtualVariable() {
|
||||
this = TUnknownVirtualVariable(f)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "UnknownVvar(" + f + ")"
|
||||
}
|
||||
|
||||
override final string getUniqueId() {
|
||||
result = "UnknownVvar(" + f + ")"
|
||||
}
|
||||
|
||||
override final Type getType() {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
|
||||
final FunctionIR getFunctionIR() {
|
||||
result = f
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TMemoryAccess =
|
||||
TVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) {
|
||||
exists(Instruction instr |
|
||||
exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, offset) and
|
||||
if exists(instr.getResultSize())
|
||||
then instr.getResultSize() = size
|
||||
else size = Ints::unknown()
|
||||
)
|
||||
}
|
||||
or
|
||||
TUnknownMemoryAccess(UnknownVirtualVariable uvv) or
|
||||
TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv)
|
||||
|
||||
private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) {
|
||||
result.getVariable() = var and
|
||||
result.getOffset() = offset and
|
||||
result.getSize() = size
|
||||
}
|
||||
|
||||
class MemoryAccess extends TMemoryAccess {
|
||||
string toString() {
|
||||
none()
|
||||
}
|
||||
|
||||
VirtualVariable getVirtualVariable() {
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isPartialMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
|
||||
IRVariable var;
|
||||
IntValue offset;
|
||||
IntValue size;
|
||||
|
||||
VariableMemoryAccess() {
|
||||
this = TVariableMemoryAccess(var, offset, size)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = var.toString() + "[" + offset.toString() + ".." + (offset + size - 1).toString() + "]"
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
result = getVirtualVariable(var) or
|
||||
not exists(getVirtualVariable(var)) and result = getUnknownVirtualVariable(var.getFunctionIR())
|
||||
}
|
||||
|
||||
IntValue getOffset() {
|
||||
result = offset
|
||||
}
|
||||
|
||||
IntValue getSize() {
|
||||
result = size
|
||||
}
|
||||
|
||||
final IRVariable getVariable() {
|
||||
result = var
|
||||
}
|
||||
|
||||
final override predicate isPartialMemoryAccess() {
|
||||
not exists(getVirtualVariable(var)) or
|
||||
getOffset() != 0
|
||||
or
|
||||
getSize() != var.getType().getSize()
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess {
|
||||
UnknownVirtualVariable vvar;
|
||||
|
||||
UnknownMemoryAccess() {
|
||||
this = TUnknownMemoryAccess(vvar)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result = vvar.toString()
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
|
||||
final override predicate isPartialMemoryAccess() {
|
||||
any()
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
}
|
||||
|
||||
class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess {
|
||||
UnknownVirtualVariable vvar;
|
||||
|
||||
TotalUnknownMemoryAccess() {
|
||||
this = TTotalUnknownMemoryAccess(vvar)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result = vvar.toString()
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
def instanceof VariableMemoryAccess and
|
||||
def = use and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
exists(VariableMemoryAccess defVMA, VariableMemoryAccess useVMA, int defOffset, int defEnd,
|
||||
int useOffset, int useEnd |
|
||||
defVMA = def and
|
||||
useVMA = use and
|
||||
defVMA.getVirtualVariable() = useVMA.getVirtualVariable() and
|
||||
defVMA != useVMA and
|
||||
defOffset = Ints::getValue(defVMA.getOffset()) and
|
||||
defEnd = Ints::getValue(Ints::add(defVMA.getOffset(), defVMA.getSize())) and
|
||||
useOffset = Ints::getValue(useVMA.getOffset()) and
|
||||
useEnd = Ints::getValue(Ints::add(useVMA.getOffset(), useVMA.getSize()))
|
||||
|
|
||||
defOffset <= useOffset and
|
||||
defEnd >= useEnd and
|
||||
result instanceof MustTotallyOverlap
|
||||
or
|
||||
defOffset > useOffset and
|
||||
defOffset < useEnd and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
defOffset = useOffset and
|
||||
defEnd < useEnd and
|
||||
result instanceof MayPartiallyOverlap
|
||||
)
|
||||
or
|
||||
exists(UnknownVirtualVariable uvv |
|
||||
def = TUnknownMemoryAccess(uvv) and
|
||||
uvv = use.getVirtualVariable() and
|
||||
result instanceof MayPartiallyOverlap
|
||||
)
|
||||
or
|
||||
exists(UnknownVirtualVariable uvv |
|
||||
def = TTotalUnknownMemoryAccess(uvv) and
|
||||
uvv = use.getVirtualVariable() and
|
||||
result instanceof MustTotallyOverlap
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
exists(instr.getResultMemoryAccess()) and
|
||||
if exists(IRVariable var, IntValue i |
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i)
|
||||
)
|
||||
then exists(IRVariable var, IntValue i |
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i) and
|
||||
result = getVariableMemoryAccess(var, i, instr.getResultSize())
|
||||
)
|
||||
else (
|
||||
result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and
|
||||
not instr instanceof UnmodeledDefinitionInstruction and
|
||||
not instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
exists(operand.getMemoryAccess()) and
|
||||
if exists(IRVariable var, IntValue i |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i)
|
||||
)
|
||||
then exists(IRVariable var, IntValue i, int size |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and
|
||||
result = getVariableMemoryAccess(var, i, size) and
|
||||
size = operand.getDefinitionInstruction().getResultSize()
|
||||
)
|
||||
else (
|
||||
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR())) and
|
||||
not operand.getInstruction() instanceof UnmodeledUseInstruction
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstruction as Construction
|
||||
private import NewIR
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
|
||||
|
||||
private OldIR::IRBlock getOldBlock(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getInstruction(index)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = getOldBlock(block).getInstructionCount()
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
|
||||
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
|
||||
}
|
||||
|
||||
cached Instruction getFirstInstruction(TIRBlock block) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getFirstInstruction()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import AliasedSSA
|
||||
|
||||
/**
|
||||
* Property provide that dumps the memory access of each result. Useful for debugging SSA
|
||||
* construction.
|
||||
*/
|
||||
class PropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
key = "ResultMemoryAccess" and result = getResultMemoryAccess(instruction).toString()
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,29 @@ import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
WrappedInstructionTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
}
|
||||
} or
|
||||
ChiTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
UnreachedTag()
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
cached final string toString() {
|
||||
@@ -32,20 +39,36 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
cached OldInstruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
|
||||
private Instruction getNewInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewInstruction(OldInstruction instr) {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
/**
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldInstruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldInstruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
result.getFunction() = var.getFunction() and
|
||||
@@ -72,8 +95,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldIR::Instruction instr |
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldInstruction instr |
|
||||
instr.getFunction() = func and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getAST() = ast and
|
||||
@@ -84,7 +107,7 @@ cached private module Cached {
|
||||
else
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
|
||||
exists(OldBlock block, Alias::VirtualVariable vvar |
|
||||
hasPhiNode(vvar, block) and
|
||||
block.getFunction() = func and
|
||||
opcode instanceof Opcode::Phi and
|
||||
@@ -92,11 +115,29 @@ cached private module Cached {
|
||||
tag = PhiTag(vvar, block) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction instr, Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, instr) and
|
||||
instr.getFunction() = func and
|
||||
opcode instanceof Opcode::Chi and
|
||||
ast = instr.getAST() and
|
||||
tag = ChiTag(instr) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction oldInstruction |
|
||||
func = oldInstruction.getFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) and
|
||||
tag = UnreachedTag() and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
ast = func and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
|
||||
Type type) {
|
||||
Type type) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getFunction() = func and
|
||||
var.getAST() = ast and
|
||||
@@ -107,36 +148,37 @@ cached private module Cached {
|
||||
|
||||
cached predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
|
||||
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
|
||||
)
|
||||
)
|
||||
)
|
||||
else (
|
||||
result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
) or
|
||||
// Connect any definitions that are not being modeled in SSA to the
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
exists(OldInstruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
@@ -146,28 +188,53 @@ cached private module Cached {
|
||||
)
|
||||
else
|
||||
result = getNewInstruction(oldOperand.getDefinitionInstruction())
|
||||
)
|
||||
) or
|
||||
instruction.getTag() = ChiTag(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag
|
||||
or
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
result instanceof UnmodeledDefinitionInstruction and
|
||||
instruction.getFunction() = result.getFunction()
|
||||
or
|
||||
tag instanceof ChiTotalOperandTag and
|
||||
result = getChiInstructionTotalOperand(instruction)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
|
||||
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
predBlock = phiBlock.getAFeasiblePredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(instr.getFunction(), defBlock, vvar)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
|
||||
int defRank, int defIndex, OldBlock useBlock, int useRank |
|
||||
ChiTag(oldInstr) = chiInstr.getTag() and
|
||||
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldIR::IRBlock oldBlock |
|
||||
exists(OldBlock oldBlock |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
@@ -181,8 +248,34 @@ cached private module Cached {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
if(hasChiNode(_, getOldInstruction(instruction)))
|
||||
then
|
||||
result = getChiInstruction(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) then (
|
||||
result.getTag() = UnreachedTag() and
|
||||
result.getFunction() = instruction.getFunction()
|
||||
)
|
||||
else (
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
) or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached IRVariable getInstructionVariable(Instruction instruction) {
|
||||
@@ -228,38 +321,59 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(OldIR::SideEffectInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
result = getNewInstruction(oldInstruction.getPrimaryInstruction())
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction.getTag() = ChiTag(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
|
||||
OldInstruction instr, OldBlock block, int index) {
|
||||
block.getInstruction(index) = instr and
|
||||
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
|
||||
}
|
||||
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
|
||||
(
|
||||
hasPhiNode(vvar, block) and
|
||||
index = -1
|
||||
) or
|
||||
exists(Alias::MemoryAccess access, OldIR::Instruction def |
|
||||
exists(Alias::MemoryAccess access, OldInstruction def |
|
||||
access = Alias::getResultMemoryAccess(def) and
|
||||
block.getInstruction(index) = def and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
|
||||
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
|
||||
}
|
||||
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
|
||||
int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
|
||||
(
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand())
|
||||
or
|
||||
/*
|
||||
* a partial write to a virtual variable is going to generate a use of that variable when
|
||||
* Chi nodes are inserted, so we need to mark it as a use in the old IR
|
||||
*/
|
||||
access = Alias::getResultMemoryAccess(use) and
|
||||
access.isPartialMemoryAccess()
|
||||
) and
|
||||
block.getInstruction(index) = use and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
exists (int index | hasUse(vvar, _, block, index) |
|
||||
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
|
||||
) or
|
||||
@@ -267,8 +381,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,18 +391,18 @@ cached private module Cached {
|
||||
* end of the block, even if the definition is the last instruction in the
|
||||
* block.
|
||||
*/
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
|
||||
}
|
||||
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
int instructionIndex) {
|
||||
hasDefinition(vvar, block, instructionIndex) and
|
||||
defUseRank(vvar, block, rankIndex, instructionIndex)
|
||||
}
|
||||
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
|
||||
int rankIndex, OldIR::Instruction use) {
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
OldInstruction use) {
|
||||
exists(int index |
|
||||
hasUse(vvar, use, block, index) and
|
||||
defUseRank(vvar, block, rankIndex, index)
|
||||
@@ -299,8 +413,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
|
||||
* index `reachesRank` in block `block`.
|
||||
*/
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int defRank, int reachesRank) {
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
|
||||
int reachesRank) {
|
||||
hasDefinitionAtRank(vvar, block, defRank, _) and
|
||||
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
@@ -320,8 +434,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
|
||||
* block `block`.
|
||||
*/
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock block) {
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
(
|
||||
(
|
||||
@@ -331,7 +445,7 @@ cached private module Cached {
|
||||
variableLiveOnExitFromBlock(vvar, defBlock) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
|
||||
) or
|
||||
exists(OldIR::IRBlock idom |
|
||||
exists(OldBlock idom |
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
|
||||
noDefinitionsSinceIDominator(vvar, idom, block)
|
||||
)
|
||||
@@ -339,51 +453,56 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock idom, OldIR::IRBlock block) {
|
||||
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
|
||||
OldBlock block) {
|
||||
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
variableLiveOnExitFromBlock(vvar, block) and
|
||||
not hasDefinition(vvar, block, _)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUseWithinBlock(
|
||||
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
|
||||
OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
defBlock = useBlock and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, useRank)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
(
|
||||
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
|
||||
useRank) or
|
||||
(
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank,
|
||||
useBlock.getAPredecessor()) and
|
||||
useBlock.getAFeasiblePredecessor()) and
|
||||
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
exists(OldIR::IRBlock defBlock |
|
||||
phiBlock = defBlock.dominanceFrontier() and
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
hasDefinition(vvar, defBlock, _) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
variableLiveOnEntryToBlock(vvar, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
ma.getVirtualVariable() = vvar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
@@ -393,13 +512,17 @@ cached private module CachedForDebugging {
|
||||
}
|
||||
|
||||
cached string getInstructionUniqueId(Instruction instr) {
|
||||
exists(OldIR::Instruction oldInstr |
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
result = "NonSSA: " + oldInstr.getUniqueId()
|
||||
) or
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
|
||||
) or
|
||||
(
|
||||
instr.getTag() = UnreachedTag() and
|
||||
result = "Unreached"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import SimpleSSA as Alias
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import SimpleSSAInternal
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
private newtype TVirtualVariable =
|
||||
MkVirtualVariable(IRVariable var) {
|
||||
not variableAddressEscapes(var)
|
||||
}
|
||||
|
||||
private VirtualVariable getVirtualVariable(IRVariable var) {
|
||||
result.getIRVariable() = var
|
||||
}
|
||||
|
||||
class VirtualVariable extends TVirtualVariable {
|
||||
IRVariable var;
|
||||
|
||||
VirtualVariable() {
|
||||
this = MkVirtualVariable(var)
|
||||
}
|
||||
|
||||
final string toString() {
|
||||
result = var.toString()
|
||||
}
|
||||
|
||||
final IRVariable getIRVariable() {
|
||||
result = var
|
||||
}
|
||||
|
||||
// REVIEW: This should just be on MemoryAccess
|
||||
final Type getType() {
|
||||
result = var.getType()
|
||||
}
|
||||
|
||||
final string getUniqueId() {
|
||||
result = var.getUniqueId()
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TMemoryAccess =
|
||||
MkMemoryAccess(VirtualVariable vvar)
|
||||
|
||||
private MemoryAccess getMemoryAccess(IRVariable var) {
|
||||
result.getVirtualVariable() = getVirtualVariable(var)
|
||||
}
|
||||
|
||||
class MemoryAccess extends TMemoryAccess {
|
||||
VirtualVariable vvar;
|
||||
|
||||
MemoryAccess() {
|
||||
this = MkMemoryAccess(vvar)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = vvar.toString()
|
||||
}
|
||||
|
||||
VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
result instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
exists(IRVariable var |
|
||||
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
|
||||
var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
exists(IRVariable var |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import AliasAnalysis as Alias
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
* sequence.
|
||||
*
|
||||
* This class does not contain any members that query the predecessor or successor edges of the
|
||||
* block. This allows different classes that extend `IRBlockBase` to expose different subsets of
|
||||
* edges (e.g. ignoring unreachable edges).
|
||||
*
|
||||
* Most consumers should use the class `IRBlock`.
|
||||
*/
|
||||
class IRBlockBase extends TIRBlock {
|
||||
final string toString() {
|
||||
result = getFirstInstruction(this).toString()
|
||||
}
|
||||
@@ -59,7 +70,14 @@ class IRBlock extends TIRBlock {
|
||||
final Function getFunction() {
|
||||
result = getFirstInstruction(this).getFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block with additional information about its predecessor and successor edges. Each edge
|
||||
* corresponds to the control flow between the last instruction of one block and the first
|
||||
* instruction of another block.
|
||||
*/
|
||||
class IRBlock extends IRBlockBase {
|
||||
final IRBlock getASuccessor() {
|
||||
blockSuccessor(this, result)
|
||||
}
|
||||
@@ -98,3 +116,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
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 MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -95,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*/
|
||||
final string getOperationString() {
|
||||
if exists(getImmediateString()) then
|
||||
result = opcode.toString() + "[" + getImmediateString() + "]"
|
||||
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
|
||||
else
|
||||
result = opcode.toString()
|
||||
result = getOperationPrefix() + opcode.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction {
|
||||
none()
|
||||
}
|
||||
|
||||
private string getOperationPrefix() {
|
||||
if this instanceof SideEffectInstruction then
|
||||
result = "^"
|
||||
else
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if resultType instanceof VoidType then
|
||||
result = "v"
|
||||
@@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class SideEffectInstruction extends Instruction {
|
||||
SideEffectInstruction() {
|
||||
opcode instanceof SideEffectOpcode
|
||||
}
|
||||
|
||||
final Instruction getPrimaryInstruction() {
|
||||
result = Construction::getPrimaryInstructionForSideEffect(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be
|
||||
* accessed by that call.
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be read
|
||||
* by that call.
|
||||
*/
|
||||
class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
CallReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferMayWriteSideEffect
|
||||
}
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes all escaped memory.
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() {
|
||||
opcode instanceof Opcode::AliasedDefinition
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() {
|
||||
opcode instanceof Opcode::UnmodeledUse
|
||||
@@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the choice of one of multiple input values based on control flow.
|
||||
*
|
||||
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
|
||||
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
|
||||
* each control flow predecessor of the block, with that operand representing the version of the
|
||||
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
|
||||
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
|
||||
* runtime.
|
||||
*/
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() {
|
||||
opcode instanceof Opcode::Phi
|
||||
@@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the effect that a write to a memory may have on potential aliases of
|
||||
* that memory.
|
||||
*
|
||||
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
|
||||
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
|
||||
* the previous state of all of the memory that might be alised by the memory write. The second
|
||||
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
|
||||
* memory write. The result of the `ChiInstruction` represents the same memory as
|
||||
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
|
||||
* the memory write.
|
||||
*
|
||||
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
|
||||
* same memory:
|
||||
* ```
|
||||
* *p = 5;
|
||||
* x = *q;
|
||||
* ```
|
||||
*
|
||||
* The IR would look like:
|
||||
* ```
|
||||
* r1_1 = VariableAddress[p]
|
||||
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
|
||||
* r1_3 = Constant[5]
|
||||
* m1_4 = Store r1_2, r1_3 // Store to `*p`
|
||||
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
|
||||
* r1_6 = VariableAddress[x]
|
||||
* r1_7 = VariableAddress[q]
|
||||
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
|
||||
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
|
||||
* m1_10 = Store r1_6, r1_9 // Store to x
|
||||
* ```
|
||||
*
|
||||
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
|
||||
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
|
||||
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
|
||||
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
|
||||
* pointed to by `q`.
|
||||
*
|
||||
* For more information about how `Chi` instructions are used to model memory side effects, see
|
||||
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
|
||||
*/
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() {
|
||||
opcode instanceof Opcode::Chi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
*/
|
||||
final Instruction getTotalOperand() {
|
||||
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the new value written by the memory write.
|
||||
*/
|
||||
final Instruction getPartialOperand() {
|
||||
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
SideEffectOperand() {
|
||||
this = TNonPhiOperand(_, sideEffectOperand(), _)
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
instr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
or
|
||||
instr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,3 +397,38 @@ class MemoryOperand extends Operand {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends Operand {
|
||||
ChiTotalOperand() {
|
||||
this = TNonPhiOperand(_, chiTotalOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends Operand {
|
||||
ChiPartialOperand() {
|
||||
this = TNonPhiOperand(_, chiPartialOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiPartialMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
@@ -1,83 +0,0 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.Instruction
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
@@ -168,6 +167,13 @@ cached private module Cached {
|
||||
result = element.getInstructionResultSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
@@ -40,9 +40,11 @@ newtype TInstructionTag =
|
||||
ExitFunctionTag() or
|
||||
UnmodeledDefinitionTag() or
|
||||
UnmodeledUseTag() or
|
||||
AliasedDefinitionTag() or
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
CallSideEffectTag() or
|
||||
AllocationSizeTag() or
|
||||
AllocationElementSizeTag() or
|
||||
AllocationExtentConvertTag() or
|
||||
@@ -61,6 +63,7 @@ newtype TInstructionTag =
|
||||
CatchTag() or
|
||||
ThrowTag() or
|
||||
UnwindTag() or
|
||||
InitializerUninitializedTag() or
|
||||
InitializerFieldAddressTag(Field field) {
|
||||
fieldIsInitialized(field)
|
||||
} or
|
||||
@@ -91,6 +94,7 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load)
|
||||
tag = InitializerVariableAddressTag() and result = "InitVarAddr" or
|
||||
tag = InitializerStoreTag() and result = "InitStore" or
|
||||
tag = InitializerUninitializedTag() and result = "InitUninit" or
|
||||
tag = ZeroPadStringConstantTag() and result = "ZeroPadConst" or
|
||||
tag = ZeroPadStringElementIndexTag() and result = "ZeroPadElemIndex" or
|
||||
tag = ZeroPadStringElementAddressTag() and result = "ZeroPadElemAddr" or
|
||||
@@ -110,9 +114,11 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = ExitFunctionTag() and result = "ExitFunc" or
|
||||
tag = UnmodeledDefinitionTag() and result = "UnmodeledDef" or
|
||||
tag = UnmodeledUseTag() and result = "UnmodeledUse" or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef" or
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch" or
|
||||
tag = CallTargetTag() and result = "CallTarget" or
|
||||
tag = CallTag() and result = "Call" or
|
||||
tag = CallSideEffectTag() and result = "CallSideEffect" or
|
||||
tag = AllocationSizeTag() and result = "AllocSize" or
|
||||
tag = AllocationElementSizeTag() and result = "AllocElemSize" or
|
||||
tag = AllocationExtentConvertTag() and result = "AllocExtConv" or
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
* call in the source code, or could be a call that is part of the translation
|
||||
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
|
||||
*/
|
||||
abstract class TranslatedCall extends TranslatedExpr {
|
||||
override final TranslatedElement getChild(int id) {
|
||||
// We choose the child's id in the order of evaluation.
|
||||
// The qualifier is evaluated before the call target, because the value of
|
||||
// the call target may depend on the value of the qualifier for virtual
|
||||
// calls.
|
||||
id = -2 and result = getQualifier() or
|
||||
id = -1 and result = getCallTarget() or
|
||||
result = getArgument(id)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(getQualifier()) then
|
||||
result = getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
(
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getCallResultType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
(
|
||||
if hasWriteSideEffect() then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType
|
||||
)
|
||||
else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType instanceof VoidType
|
||||
)
|
||||
) and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getQualifier() and
|
||||
result = getFirstCallTargetInstruction()
|
||||
) or
|
||||
(
|
||||
child = getCallTarget() and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
) or
|
||||
exists(int argIndex |
|
||||
child = getArgument(argIndex) and
|
||||
if exists(getArgument(argIndex + 1)) then
|
||||
result = getArgument(argIndex + 1).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
tag = CallTag() and
|
||||
if hasSideEffect() then
|
||||
result = getInstruction(CallSideEffectTag())
|
||||
else
|
||||
result = getParent().getChildSuccessor(this)
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = CallTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = getCallTargetResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = getQualifierResult()
|
||||
) or
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = CallSideEffectTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result type of the call.
|
||||
*/
|
||||
abstract Type getCallResultType();
|
||||
|
||||
/**
|
||||
* Holds if the call has a `this` argument.
|
||||
*/
|
||||
predicate hasQualifier() {
|
||||
exists(getQualifier())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
|
||||
*/
|
||||
TranslatedExpr getCallTarget() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first instruction of the sequence to evaluate the call target.
|
||||
* By default, this is just the first instruction of `getCallTarget()`, but
|
||||
* it can be overridden by a subclass for cases where there is a call target
|
||||
* that is not computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getFirstCallTargetInstruction() {
|
||||
result = getCallTarget().getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the target of the call. By
|
||||
* default, this is just the result of `getCallTarget()`, but it can be
|
||||
* overridden by a subclass for cases where there is a call target that is not
|
||||
* computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getCallTargetResult() {
|
||||
result = getCallTarget().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
|
||||
* that is passed as the `this` argument.
|
||||
*/
|
||||
abstract TranslatedExpr getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
* By default, this is just the result of `getQualifier()`, but it can be
|
||||
* overridden by a subclass for cases where there is a `this` argument that is
|
||||
* not computed from a child expression (e.g. a constructor call).
|
||||
*/
|
||||
Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument.
|
||||
*/
|
||||
abstract TranslatedExpr getArgument(int index);
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
* argument. Otherwise, returns the call instruction.
|
||||
*/
|
||||
final Instruction getFirstArgumentOrCallInstruction() {
|
||||
if hasArguments() then
|
||||
result = getArgument(0).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
|
||||
predicate hasReadSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
predicate hasWriteSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
private predicate hasSideEffect() {
|
||||
hasReadSideEffect() or hasWriteSideEffect()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a direct call to a specific function. Used for both
|
||||
* explicit calls (`TranslatedFunctionCall`) and implicit calls
|
||||
* (`TranslatedAllocatorCall`).
|
||||
*/
|
||||
abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
override final Instruction getFirstCallTargetInstruction() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override final Instruction getCallTargetResult() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// The database does not contain a `FunctionType` for a function unless
|
||||
// its address was taken, so we'll just use glval<Unknown> instead of
|
||||
// glval<FunctionType>.
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function.
|
||||
*/
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
|
||||
TranslatedCall {
|
||||
Call call;
|
||||
|
||||
TranslatedCallExpr() {
|
||||
expr = call
|
||||
}
|
||||
|
||||
override final Type getCallResultType() {
|
||||
result = getResultType()
|
||||
}
|
||||
|
||||
override final predicate hasArguments() {
|
||||
exists(call.getArgument(0))
|
||||
}
|
||||
|
||||
override final TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
override final TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call through a function pointer.
|
||||
*/
|
||||
class TranslatedExprCall extends TranslatedCallExpr {
|
||||
ExprCall exprCall;
|
||||
|
||||
TranslatedExprCall() {
|
||||
expr = exprCall
|
||||
}
|
||||
|
||||
override TranslatedExpr getCallTarget() {
|
||||
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a direct function call.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
FunctionCall funcCall;
|
||||
|
||||
TranslatedFunctionCall() {
|
||||
expr = funcCall
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
not funcCall.getTarget().(SideEffectFunction).neverReadsMemory()
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
not funcCall.getTarget().(SideEffectFunction).neverWritesMemory()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call to a constructor.
|
||||
*/
|
||||
class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
TranslatedStructorCall() {
|
||||
funcCall instanceof ConstructorCall or
|
||||
funcCall instanceof DestructorCall
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
exists(StructorCallContext context |
|
||||
context = getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasQualifier() {
|
||||
any()
|
||||
}
|
||||
}
|
||||
@@ -142,7 +142,10 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
(
|
||||
tag = InitializerVariableAddressTag() or
|
||||
hasUninitializedInstruction() and tag = InitializerStoreTag()
|
||||
) and
|
||||
result = getIRUserVariable(getFunction(), getVariable())
|
||||
}
|
||||
|
||||
|
||||
@@ -534,6 +534,14 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
result = getParent().getExceptionSuccessorInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary instruction for the side effect instruction that was
|
||||
* generated by this element for tag `tag`.
|
||||
*/
|
||||
Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element generates a temporary variable with type `type`.
|
||||
* `tag` must be unique for each variable generated from the same AST node
|
||||
|
||||
@@ -8,6 +8,7 @@ private import TranslatedDeclarationEntry
|
||||
private import TranslatedElement
|
||||
private import TranslatedFunction
|
||||
private import TranslatedInitialization
|
||||
import TranslatedCall
|
||||
|
||||
/**
|
||||
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
|
||||
@@ -1769,163 +1770,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
* call in the source code, or could be a call that is part of the translation
|
||||
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
|
||||
*/
|
||||
abstract class TranslatedCall extends TranslatedExpr {
|
||||
override final TranslatedElement getChild(int id) {
|
||||
// We choose the child's id in the order of evaluation.
|
||||
// The qualifier is evaluated before the call target, because the value of
|
||||
// the call target may depend on the value of the qualifier for virtual
|
||||
// calls.
|
||||
id = -2 and result = getQualifier() or
|
||||
id = -1 and result = getCallTarget() or
|
||||
result = getArgument(id)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(getQualifier()) then
|
||||
result = getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getCallResultType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getQualifier() and
|
||||
result = getFirstCallTargetInstruction()
|
||||
) or
|
||||
(
|
||||
child = getCallTarget() and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
) or
|
||||
exists(int argIndex |
|
||||
child = getArgument(argIndex) and
|
||||
if exists(getArgument(argIndex + 1)) then
|
||||
result = getArgument(argIndex + 1).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
tag = CallTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = CallTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = getCallTargetResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = getQualifierResult()
|
||||
) or
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result type of the call.
|
||||
*/
|
||||
abstract Type getCallResultType();
|
||||
|
||||
/**
|
||||
* Holds if the call has a `this` argument.
|
||||
*/
|
||||
predicate hasQualifier() {
|
||||
exists(getQualifier())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
|
||||
*/
|
||||
TranslatedExpr getCallTarget() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first instruction of the sequence to evaluate the call target.
|
||||
* By default, this is just the first instruction of `getCallTarget()`, but
|
||||
* it can be overridden by a subclass for cases where there is a call target
|
||||
* that is not computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getFirstCallTargetInstruction() {
|
||||
result = getCallTarget().getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the target of the call. By
|
||||
* default, this is just the result of `getCallTarget()`, but it can be
|
||||
* overridden by a subclass for cases where there is a call target that is not
|
||||
* computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getCallTargetResult() {
|
||||
result = getCallTarget().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
|
||||
* that is passed as the `this` argument.
|
||||
*/
|
||||
abstract TranslatedExpr getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
* By default, this is just the result of `getQualifier()`, but it can be
|
||||
* overridden by a subclass for cases where there is a `this` argument that is
|
||||
* not computed from a child expression (e.g. a constructor call).
|
||||
*/
|
||||
Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument.
|
||||
*/
|
||||
abstract TranslatedExpr getArgument(int index);
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
* argument. Otherwise, returns the call instruction.
|
||||
*/
|
||||
final Instruction getFirstArgumentOrCallInstruction() {
|
||||
if hasArguments() then
|
||||
result = getArgument(0).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the allocation size argument passed to `operator new`
|
||||
* in a `new` expression.
|
||||
@@ -2089,45 +1933,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a direct call to a specific function. Used for both
|
||||
* explicit calls (`TranslatedFunctionCall`) and implicit calls
|
||||
* (`TranslatedAllocatorCall`).
|
||||
*/
|
||||
abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
override final Instruction getFirstCallTargetInstruction() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override final Instruction getCallTargetResult() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// The database does not contain a `FunctionType` for a function unless
|
||||
// its address was taken, so we'll just use glval<Unknown> instead of
|
||||
// glval<FunctionType>.
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to `operator new` as part of a `new` or `new[]`
|
||||
* expression.
|
||||
@@ -2185,65 +1990,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall,
|
||||
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
|
||||
result.getAST() = newExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function.
|
||||
*/
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
|
||||
TranslatedCall {
|
||||
Call call;
|
||||
|
||||
TranslatedCallExpr() {
|
||||
expr = call
|
||||
}
|
||||
|
||||
override final Type getCallResultType() {
|
||||
result = getResultType()
|
||||
}
|
||||
|
||||
override final predicate hasArguments() {
|
||||
exists(call.getArgument(0))
|
||||
}
|
||||
|
||||
override final TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
override final TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call through a function pointer.
|
||||
*/
|
||||
class TranslatedExprCall extends TranslatedCallExpr {
|
||||
ExprCall exprCall;
|
||||
|
||||
TranslatedExprCall() {
|
||||
expr = exprCall
|
||||
}
|
||||
|
||||
override TranslatedExpr getCallTarget() {
|
||||
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a direct function call.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
FunctionCall funcCall;
|
||||
|
||||
TranslatedFunctionCall() {
|
||||
expr = funcCall
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class implemented by any `TranslatedElement` that has a child
|
||||
* expression that is a call to a constructor or destructor, in order to
|
||||
@@ -2257,27 +2003,6 @@ abstract class StructorCallContext extends TranslatedElement {
|
||||
abstract Instruction getReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call to a constructor.
|
||||
*/
|
||||
class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
TranslatedStructorCall() {
|
||||
funcCall instanceof ConstructorCall or
|
||||
funcCall instanceof DestructorCall
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
exists(StructorCallContext context |
|
||||
context = getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasQualifier() {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the destruction of a field from within
|
||||
* the destructor of the field's declaring class.
|
||||
|
||||
@@ -76,6 +76,9 @@ class TranslatedFunction extends TranslatedElement,
|
||||
(
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
result = getInstruction(AliasedDefinitionTag())
|
||||
) or (
|
||||
tag = AliasedDefinitionTag() and
|
||||
result = getInstruction(UnmodeledDefinitionTag())
|
||||
) or
|
||||
(
|
||||
@@ -153,6 +156,12 @@ class TranslatedFunction extends TranslatedElement,
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
@@ -218,6 +227,11 @@ class TranslatedFunction extends TranslatedElement,
|
||||
result.getFunction() = func and
|
||||
result.hasMemoryResult()
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
) or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
not getReturnType() instanceof VoidType and
|
||||
|
||||
@@ -148,7 +148,11 @@ class TranslatedArrayListInitialization extends
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
result = getTranslatedElementInitialization(initList, id)
|
||||
// The children are in initialization order
|
||||
result = rank[id + 1](TranslatedElementInitialization init |
|
||||
init.getInitList() = initList |
|
||||
init order by init.getElementIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,15 +672,6 @@ class TranslatedFieldValueInitialization extends
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedElementInitialization` for element `elementIndex` in
|
||||
* initializer list `initList`.
|
||||
*/
|
||||
TranslatedElementInitialization getTranslatedElementInitialization(
|
||||
ArrayAggregateLiteral initList, int elementIndex) {
|
||||
result.getInitList() = initList and result.getElementIndex() = elementIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an element of an initializer list.
|
||||
@@ -717,7 +712,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
tag = getElementIndexTag() and
|
||||
result = getInstruction(getElementAddressTag()) and
|
||||
kind instanceof GotoEdge
|
||||
@@ -767,9 +762,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an explicit element in an initializer list.
|
||||
*/
|
||||
class TranslatedExplicitElementInitialization extends
|
||||
TranslatedElementInitialization, TTranslatedExplicitElementInitialization,
|
||||
InitializationContext {
|
||||
class TranslatedExplicitElementInitialization extends TranslatedElementInitialization,
|
||||
TTranslatedExplicitElementInitialization, InitializationContext {
|
||||
int elementIndex;
|
||||
|
||||
TranslatedExplicitElementInitialization() {
|
||||
@@ -785,7 +779,7 @@ class TranslatedExplicitElementInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = getElementAddressTag() and
|
||||
@@ -816,8 +810,8 @@ class TranslatedExplicitElementInitialization extends
|
||||
* Represents the IR translation of the initialization of a range of array
|
||||
* elements without corresponding elements in the initializer list.
|
||||
*/
|
||||
class TranslatedElementValueInitialization extends
|
||||
TranslatedElementInitialization, TTranslatedElementValueInitialization {
|
||||
class TranslatedElementValueInitialization extends TranslatedElementInitialization,
|
||||
TTranslatedElementValueInitialization {
|
||||
int elementIndex;
|
||||
int elementCount;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
|
||||
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
|
||||
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
|
||||
|
||||
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockImmediatelyDominates+(dominator, block)
|
||||
}
|
||||
|
||||
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockStrictlyDominates(dominator, block) or dominator = block
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
Graph::Block getDominanceFrontier(Graph::Block dominator) {
|
||||
exists(Graph::Block pred |
|
||||
Graph::blockSuccessor(pred, result) and
|
||||
blockDominates(dominator, pred) and
|
||||
not blockStrictlyDominates(dominator, result)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
private import ReachableBlock as Reachability
|
||||
private module ReachabilityGraph = Reachability::Graph;
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
private import ReachableBlockInternal
|
||||
private import Dominance
|
||||
import IR
|
||||
|
||||
private class DominancePropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
exists(IRBlock dominator |
|
||||
blockImmediatelyDominates(dominator, block) and
|
||||
key = "ImmediateDominator" and
|
||||
result = "Block " + dominator.getDisplayIndex().toString()
|
||||
) or
|
||||
(
|
||||
key = "DominanceFrontier" and
|
||||
result = strictconcat(IRBlock frontierBlock |
|
||||
frontierBlock = getDominanceFrontier(block) |
|
||||
frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
private import ReachableBlockInternal
|
||||
private import ReachableBlock
|
||||
import IR
|
||||
|
||||
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
(
|
||||
not block instanceof ReachableBlock and
|
||||
key = "Unreachable" and
|
||||
result = "true"
|
||||
) or
|
||||
(
|
||||
exists(EdgeKind kind |
|
||||
isInfeasibleEdge(block, kind) and
|
||||
key = "Infeasible(" + kind.toString() + ")" and
|
||||
result = "true"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
private import ReachableBlockInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
private import ConstantAnalysis
|
||||
|
||||
predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
exists(int conditionValue |
|
||||
conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and
|
||||
if conditionValue = 0 then
|
||||
kind instanceof TrueEdge
|
||||
else
|
||||
kind instanceof FalseEdge
|
||||
)
|
||||
}
|
||||
|
||||
predicate isInfeasibleEdge(IRBlockBase block, EdgeKind kind) {
|
||||
isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind)
|
||||
}
|
||||
|
||||
private IRBlock getAFeasiblePredecessorBlock(IRBlock successor) {
|
||||
exists(EdgeKind kind |
|
||||
result.getSuccessor(kind) = successor and
|
||||
not isInfeasibleEdge(result, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isBlockReachable(IRBlock block) {
|
||||
exists(FunctionIR f |
|
||||
getAFeasiblePredecessorBlock*(block) = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR block that is reachable from the entry block of the function, considering only feasible
|
||||
* edges.
|
||||
*/
|
||||
class ReachableBlock extends IRBlockBase {
|
||||
ReachableBlock() {
|
||||
isBlockReachable(this)
|
||||
}
|
||||
|
||||
final ReachableBlock getAFeasiblePredecessor() {
|
||||
result = getAFeasiblePredecessorBlock(this)
|
||||
}
|
||||
|
||||
final ReachableBlock getAFeasibleSuccessor() {
|
||||
this = getAFeasiblePredecessorBlock(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that is contained in a reachable block.
|
||||
*/
|
||||
class ReachableInstruction extends Instruction {
|
||||
ReachableInstruction() {
|
||||
this.getBlock() instanceof ReachableBlock
|
||||
}
|
||||
}
|
||||
|
||||
module Graph {
|
||||
predicate isEntryBlock(ReachableBlock block) {
|
||||
exists(FunctionIR f |
|
||||
block = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
|
||||
succ = pred.getAFeasibleSuccessor()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -1,9 +1,20 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
private import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
* sequence.
|
||||
*
|
||||
* This class does not contain any members that query the predecessor or successor edges of the
|
||||
* block. This allows different classes that extend `IRBlockBase` to expose different subsets of
|
||||
* edges (e.g. ignoring unreachable edges).
|
||||
*
|
||||
* Most consumers should use the class `IRBlock`.
|
||||
*/
|
||||
class IRBlockBase extends TIRBlock {
|
||||
final string toString() {
|
||||
result = getFirstInstruction(this).toString()
|
||||
}
|
||||
@@ -59,7 +70,14 @@ class IRBlock extends TIRBlock {
|
||||
final Function getFunction() {
|
||||
result = getFirstInstruction(this).getFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block with additional information about its predecessor and successor edges. Each edge
|
||||
* corresponds to the control flow between the last instruction of one block and the first
|
||||
* instruction of another block.
|
||||
*/
|
||||
class IRBlock extends IRBlockBase {
|
||||
final IRBlock getASuccessor() {
|
||||
blockSuccessor(this, result)
|
||||
}
|
||||
@@ -98,3 +116,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
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 MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -95,7 +102,8 @@ module InstructionSanity {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*/
|
||||
final string getOperationString() {
|
||||
if exists(getImmediateString()) then
|
||||
result = opcode.toString() + "[" + getImmediateString() + "]"
|
||||
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
|
||||
else
|
||||
result = opcode.toString()
|
||||
result = getOperationPrefix() + opcode.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction {
|
||||
none()
|
||||
}
|
||||
|
||||
private string getOperationPrefix() {
|
||||
if this instanceof SideEffectInstruction then
|
||||
result = "^"
|
||||
else
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if resultType instanceof VoidType then
|
||||
result = "v"
|
||||
@@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction {
|
||||
final predicate isResultModeled() {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
class UninitializedInstruction extends Instruction {
|
||||
class UninitializedInstruction extends VariableInstruction {
|
||||
UninitializedInstruction() {
|
||||
opcode instanceof Opcode::Uninitialized
|
||||
}
|
||||
@@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction {
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `LocalVariable` that is uninitialized.
|
||||
*/
|
||||
final LocalVariable getLocalVariable() {
|
||||
result = var.(IRUserVariable).getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpInstruction extends Instruction {
|
||||
@@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that computes the target function of the call. This is usually a
|
||||
* `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a
|
||||
* function pointer.
|
||||
*/
|
||||
final Instruction getCallTarget() {
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final Instruction getAnArgument() {
|
||||
result = getAnOperand().(ArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `this` pointer argument of the call, if any.
|
||||
*/
|
||||
final Instruction getThisArgument() {
|
||||
result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
exists(PositionalArgumentOperand operand |
|
||||
operand = getAnOperand() and
|
||||
operand.getIndex() = index and
|
||||
result = operand.getDefinitionInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class SideEffectInstruction extends Instruction {
|
||||
SideEffectInstruction() {
|
||||
opcode instanceof SideEffectOpcode
|
||||
}
|
||||
|
||||
final Instruction getPrimaryInstruction() {
|
||||
result = Construction::getPrimaryInstructionForSideEffect(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be
|
||||
* accessed by that call.
|
||||
*/
|
||||
class CallSideEffectInstruction extends SideEffectInstruction {
|
||||
CallSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the side effect of a function call on any memory that might be read
|
||||
* by that call.
|
||||
*/
|
||||
class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
CallReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::CallReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferReadSideEffect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() {
|
||||
opcode instanceof Opcode::BufferMayWriteSideEffect
|
||||
}
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes all escaped memory.
|
||||
*/
|
||||
class AliasedDefinitionInstruction extends Instruction {
|
||||
AliasedDefinitionInstruction() {
|
||||
opcode instanceof Opcode::AliasedDefinition
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof EscapedMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() {
|
||||
opcode instanceof Opcode::UnmodeledUse
|
||||
@@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the choice of one of multiple input values based on control flow.
|
||||
*
|
||||
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
|
||||
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
|
||||
* each control flow predecessor of the block, with that operand representing the version of the
|
||||
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
|
||||
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
|
||||
* runtime.
|
||||
*/
|
||||
class PhiInstruction extends Instruction {
|
||||
PhiInstruction() {
|
||||
opcode instanceof Opcode::Phi
|
||||
@@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the effect that a write to a memory may have on potential aliases of
|
||||
* that memory.
|
||||
*
|
||||
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
|
||||
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
|
||||
* the previous state of all of the memory that might be alised by the memory write. The second
|
||||
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
|
||||
* memory write. The result of the `ChiInstruction` represents the same memory as
|
||||
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
|
||||
* the memory write.
|
||||
*
|
||||
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
|
||||
* same memory:
|
||||
* ```
|
||||
* *p = 5;
|
||||
* x = *q;
|
||||
* ```
|
||||
*
|
||||
* The IR would look like:
|
||||
* ```
|
||||
* r1_1 = VariableAddress[p]
|
||||
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
|
||||
* r1_3 = Constant[5]
|
||||
* m1_4 = Store r1_2, r1_3 // Store to `*p`
|
||||
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
|
||||
* r1_6 = VariableAddress[x]
|
||||
* r1_7 = VariableAddress[q]
|
||||
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
|
||||
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
|
||||
* m1_10 = Store r1_6, r1_9 // Store to x
|
||||
* ```
|
||||
*
|
||||
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
|
||||
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
|
||||
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
|
||||
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
|
||||
* pointed to by `q`.
|
||||
*
|
||||
* For more information about how `Chi` instructions are used to model memory side effects, see
|
||||
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
|
||||
*/
|
||||
class ChiInstruction extends Instruction {
|
||||
ChiInstruction() {
|
||||
opcode instanceof Opcode::Chi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the previous state of all memory that might be aliased by the
|
||||
* memory write.
|
||||
*/
|
||||
final Instruction getTotalOperand() {
|
||||
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the new value written by the memory write.
|
||||
*/
|
||||
final Instruction getPartialOperand() {
|
||||
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing unreachable code. Inserted in place of the original target
|
||||
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
|
||||
* infeasible.
|
||||
*/
|
||||
class UnreachedInstruction extends Instruction {
|
||||
UnreachedInstruction() {
|
||||
opcode instanceof Opcode::Unreached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends NonPhiOperand {
|
||||
SideEffectOperand() {
|
||||
this = TNonPhiOperand(_, sideEffectOperand(), _)
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
instr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
instr instanceof BufferWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
instr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
or
|
||||
instr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,3 +397,38 @@ class MemoryOperand extends Operand {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends Operand {
|
||||
ChiTotalOperand() {
|
||||
this = TNonPhiOperand(_, chiTotalOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends Operand {
|
||||
ChiPartialOperand() {
|
||||
this = TNonPhiOperand(_, chiPartialOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiPartialMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt() or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeftOperand()) and
|
||||
right = getConstantValue(binInstr.getRightOperand()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right) or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right) or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right) or
|
||||
binInstr instanceof DivInstruction and result = div(left, right) or
|
||||
binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or
|
||||
binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or
|
||||
binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or
|
||||
binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or
|
||||
binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or
|
||||
binInstr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
) or
|
||||
exists(UnaryInstruction unaryInstr, IntValue src |
|
||||
unaryInstr = instr and
|
||||
src = getConstantValue(unaryInstr.getOperand()) and
|
||||
(
|
||||
unaryInstr instanceof NegateInstruction and result = neg(src)
|
||||
)
|
||||
) or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
@@ -1,38 +0,0 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstruction as Construction
|
||||
private import NewIR
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
|
||||
|
||||
private OldIR::IRBlock getOldBlock(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getInstruction(index)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = getOldBlock(block).getInstructionCount()
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
|
||||
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
|
||||
}
|
||||
|
||||
cached Instruction getFirstInstruction(TIRBlock block) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getFirstInstruction()
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,29 @@ import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
private class OldInstruction = Reachability::ReachableInstruction;
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
WrappedInstructionTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
}
|
||||
} or
|
||||
ChiTag(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
UnreachedTag()
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
cached final string toString() {
|
||||
@@ -32,20 +39,36 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
cached OldInstruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
|
||||
private Instruction getNewInstruction(OldIR::Instruction instr) {
|
||||
private Instruction getNewInstruction(OldInstruction instr) {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
/**
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldInstruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldInstruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
result.getFunction() = var.getFunction() and
|
||||
@@ -72,8 +95,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldIR::Instruction instr |
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
exists(OldInstruction instr |
|
||||
instr.getFunction() = func and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getAST() = ast and
|
||||
@@ -84,7 +107,7 @@ cached private module Cached {
|
||||
else
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
|
||||
exists(OldBlock block, Alias::VirtualVariable vvar |
|
||||
hasPhiNode(vvar, block) and
|
||||
block.getFunction() = func and
|
||||
opcode instanceof Opcode::Phi and
|
||||
@@ -92,11 +115,29 @@ cached private module Cached {
|
||||
tag = PhiTag(vvar, block) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction instr, Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, instr) and
|
||||
instr.getFunction() = func and
|
||||
opcode instanceof Opcode::Chi and
|
||||
ast = instr.getAST() and
|
||||
tag = ChiTag(instr) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldInstruction oldInstruction |
|
||||
func = oldInstruction.getFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) and
|
||||
tag = UnreachedTag() and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
ast = func and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
|
||||
Type type) {
|
||||
Type type) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getFunction() = func and
|
||||
var.getAST() = ast and
|
||||
@@ -107,36 +148,37 @@ cached private module Cached {
|
||||
|
||||
cached predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
|
||||
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
|
||||
)
|
||||
)
|
||||
)
|
||||
else (
|
||||
result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
) or
|
||||
// Connect any definitions that are not being modeled in SSA to the
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
exists(OldInstruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
@@ -146,28 +188,53 @@ cached private module Cached {
|
||||
)
|
||||
else
|
||||
result = getNewInstruction(oldOperand.getDefinitionInstruction())
|
||||
)
|
||||
) or
|
||||
instruction.getTag() = ChiTag(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag
|
||||
or
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
result instanceof UnmodeledDefinitionInstruction and
|
||||
instruction.getFunction() = result.getFunction()
|
||||
or
|
||||
tag instanceof ChiTotalOperandTag and
|
||||
result = getChiInstructionTotalOperand(instruction)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
|
||||
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
predBlock = phiBlock.getAFeasiblePredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(instr.getFunction(), defBlock, vvar)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
|
||||
int defRank, int defIndex, OldBlock useBlock, int useRank |
|
||||
ChiTag(oldInstr) = chiInstr.getTag() and
|
||||
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
|
||||
else
|
||||
result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldIR::IRBlock oldBlock |
|
||||
exists(OldBlock oldBlock |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
@@ -181,8 +248,34 @@ cached private module Cached {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
if(hasChiNode(_, getOldInstruction(instruction)))
|
||||
then
|
||||
result = getChiInstruction(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) then (
|
||||
result.getTag() = UnreachedTag() and
|
||||
result.getFunction() = instruction.getFunction()
|
||||
)
|
||||
else (
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
) or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached IRVariable getInstructionVariable(Instruction instruction) {
|
||||
@@ -228,38 +321,59 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(OldIR::SideEffectInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
result = getNewInstruction(oldInstruction.getPrimaryInstruction())
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction.getTag() = ChiTag(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
|
||||
OldInstruction instr, OldBlock block, int index) {
|
||||
block.getInstruction(index) = instr and
|
||||
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
|
||||
}
|
||||
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
|
||||
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
|
||||
(
|
||||
hasPhiNode(vvar, block) and
|
||||
index = -1
|
||||
) or
|
||||
exists(Alias::MemoryAccess access, OldIR::Instruction def |
|
||||
exists(Alias::MemoryAccess access, OldInstruction def |
|
||||
access = Alias::getResultMemoryAccess(def) and
|
||||
block.getInstruction(index) = def and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
|
||||
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
|
||||
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
|
||||
}
|
||||
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
|
||||
int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
|
||||
(
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand())
|
||||
or
|
||||
/*
|
||||
* a partial write to a virtual variable is going to generate a use of that variable when
|
||||
* Chi nodes are inserted, so we need to mark it as a use in the old IR
|
||||
*/
|
||||
access = Alias::getResultMemoryAccess(use) and
|
||||
access.isPartialMemoryAccess()
|
||||
) and
|
||||
block.getInstruction(index) = use and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
exists (int index | hasUse(vvar, _, block, index) |
|
||||
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
|
||||
) or
|
||||
@@ -267,8 +381,8 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
|
||||
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,18 +391,18 @@ cached private module Cached {
|
||||
* end of the block, even if the definition is the last instruction in the
|
||||
* block.
|
||||
*/
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
|
||||
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
|
||||
}
|
||||
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
|
||||
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
int instructionIndex) {
|
||||
hasDefinition(vvar, block, instructionIndex) and
|
||||
defUseRank(vvar, block, rankIndex, instructionIndex)
|
||||
}
|
||||
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
|
||||
int rankIndex, OldIR::Instruction use) {
|
||||
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
|
||||
OldInstruction use) {
|
||||
exists(int index |
|
||||
hasUse(vvar, use, block, index) and
|
||||
defUseRank(vvar, block, rankIndex, index)
|
||||
@@ -299,8 +413,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
|
||||
* index `reachesRank` in block `block`.
|
||||
*/
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock block, int defRank, int reachesRank) {
|
||||
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
|
||||
int reachesRank) {
|
||||
hasDefinitionAtRank(vvar, block, defRank, _) and
|
||||
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
@@ -320,8 +434,8 @@ cached private module Cached {
|
||||
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
|
||||
* block `block`.
|
||||
*/
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
|
||||
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock block) {
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
(
|
||||
(
|
||||
@@ -331,7 +445,7 @@ cached private module Cached {
|
||||
variableLiveOnExitFromBlock(vvar, defBlock) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
|
||||
) or
|
||||
exists(OldIR::IRBlock idom |
|
||||
exists(OldBlock idom |
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
|
||||
noDefinitionsSinceIDominator(vvar, idom, block)
|
||||
)
|
||||
@@ -339,51 +453,56 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock idom, OldIR::IRBlock block) {
|
||||
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
|
||||
OldBlock block) {
|
||||
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
variableLiveOnExitFromBlock(vvar, block) and
|
||||
not hasDefinition(vvar, block, _)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUseWithinBlock(
|
||||
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
|
||||
OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
defBlock = useBlock and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
definitionReachesRank(vvar, defBlock, defRank, useRank)
|
||||
}
|
||||
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
|
||||
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
|
||||
int defRank, OldBlock useBlock, int useRank) {
|
||||
hasUseAtRank(vvar, useBlock, useRank, _) and
|
||||
(
|
||||
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
|
||||
useRank) or
|
||||
(
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank,
|
||||
useBlock.getAPredecessor()) and
|
||||
useBlock.getAFeasiblePredecessor()) and
|
||||
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
exists(OldIR::IRBlock defBlock |
|
||||
phiBlock = defBlock.dominanceFrontier() and
|
||||
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
hasDefinition(vvar, defBlock, _) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
variableLiveOnEntryToBlock(vvar, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock phiBlock) {
|
||||
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
ma.getVirtualVariable() = vvar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
@@ -393,13 +512,17 @@ cached private module CachedForDebugging {
|
||||
}
|
||||
|
||||
cached string getInstructionUniqueId(Instruction instr) {
|
||||
exists(OldIR::Instruction oldInstr |
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
result = "NonSSA: " + oldInstr.getUniqueId()
|
||||
) or
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
|
||||
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
|
||||
) or
|
||||
(
|
||||
instr.getTag() = UnreachedTag() and
|
||||
result = "Unreached"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import SimpleSSAInternal
|
||||
import AliasAnalysis
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
@@ -60,11 +59,17 @@ class MemoryAccess extends TMemoryAccess {
|
||||
VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
|
||||
predicate isPartialMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
none() // Avoid compiler error in SSAConstruction
|
||||
}
|
||||
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
import AliasAnalysis as Alias
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as InputIR
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
|
||||
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
|
||||
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
|
||||
|
||||
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockImmediatelyDominates+(dominator, block)
|
||||
}
|
||||
|
||||
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockStrictlyDominates(dominator, block) or dominator = block
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
Graph::Block getDominanceFrontier(Graph::Block dominator) {
|
||||
exists(Graph::Block pred |
|
||||
Graph::blockSuccessor(pred, result) and
|
||||
blockDominates(dominator, pred) and
|
||||
not blockStrictlyDominates(dominator, result)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
private import ReachableBlock as Reachability
|
||||
private module ReachabilityGraph = Reachability::Graph;
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
private import DominanceInternal
|
||||
private import ReachableBlockInternal
|
||||
private import Dominance
|
||||
import IR
|
||||
|
||||
private class DominancePropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
exists(IRBlock dominator |
|
||||
blockImmediatelyDominates(dominator, block) and
|
||||
key = "ImmediateDominator" and
|
||||
result = "Block " + dominator.getDisplayIndex().toString()
|
||||
) or
|
||||
(
|
||||
key = "DominanceFrontier" and
|
||||
result = strictconcat(IRBlock frontierBlock |
|
||||
frontierBlock = getDominanceFrontier(block) |
|
||||
frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
private import ReachableBlockInternal
|
||||
private import ReachableBlock
|
||||
import IR
|
||||
|
||||
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
(
|
||||
not block instanceof ReachableBlock and
|
||||
key = "Unreachable" and
|
||||
result = "true"
|
||||
) or
|
||||
(
|
||||
exists(EdgeKind kind |
|
||||
isInfeasibleEdge(block, kind) and
|
||||
key = "Infeasible(" + kind.toString() + ")" and
|
||||
result = "true"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
private import ReachableBlockInternal
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
private import IR
|
||||
private import ConstantAnalysis
|
||||
|
||||
predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
exists(int conditionValue |
|
||||
conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and
|
||||
if conditionValue = 0 then
|
||||
kind instanceof TrueEdge
|
||||
else
|
||||
kind instanceof FalseEdge
|
||||
)
|
||||
}
|
||||
|
||||
predicate isInfeasibleEdge(IRBlockBase block, EdgeKind kind) {
|
||||
isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind)
|
||||
}
|
||||
|
||||
private IRBlock getAFeasiblePredecessorBlock(IRBlock successor) {
|
||||
exists(EdgeKind kind |
|
||||
result.getSuccessor(kind) = successor and
|
||||
not isInfeasibleEdge(result, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isBlockReachable(IRBlock block) {
|
||||
exists(FunctionIR f |
|
||||
getAFeasiblePredecessorBlock*(block) = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR block that is reachable from the entry block of the function, considering only feasible
|
||||
* edges.
|
||||
*/
|
||||
class ReachableBlock extends IRBlockBase {
|
||||
ReachableBlock() {
|
||||
isBlockReachable(this)
|
||||
}
|
||||
|
||||
final ReachableBlock getAFeasiblePredecessor() {
|
||||
result = getAFeasiblePredecessorBlock(this)
|
||||
}
|
||||
|
||||
final ReachableBlock getAFeasibleSuccessor() {
|
||||
this = getAFeasiblePredecessorBlock(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that is contained in a reachable block.
|
||||
*/
|
||||
class ReachableInstruction extends Instruction {
|
||||
ReachableInstruction() {
|
||||
this.getBlock() instanceof ReachableBlock
|
||||
}
|
||||
}
|
||||
|
||||
module Graph {
|
||||
predicate isEntryBlock(ReachableBlock block) {
|
||||
exists(FunctionIR f |
|
||||
block = f.getEntryBlock()
|
||||
)
|
||||
}
|
||||
|
||||
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
|
||||
succ = pred.getAFeasibleSuccessor()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -102,6 +102,96 @@ IntValue div(IntValue a, IntValue b) {
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a == b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareEQ(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a = b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a != b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareNE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a != b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a < b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a < b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a > b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a > b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a <= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a <= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a >= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a >= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `-a`. If `a` is unknown, the result is unknown.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,8 @@ private int getMaxCallArgIndex() {
|
||||
|
||||
private newtype TOperandTag =
|
||||
TAddressOperand() or
|
||||
TBufferSizeOperand() or
|
||||
TSideEffectOperand() or
|
||||
TCopySourceOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
@@ -25,7 +27,9 @@ private newtype TOperandTag =
|
||||
exists(BuiltInOperation op |
|
||||
exists(op.getChild(argIndex))
|
||||
)
|
||||
}
|
||||
} or
|
||||
TChiTotalOperand() or
|
||||
TChiPartialOperand()
|
||||
|
||||
/**
|
||||
* Identifies the kind of operand on an instruction. Each `Instruction` has at
|
||||
@@ -47,7 +51,7 @@ abstract class OperandTag extends TOperandTag {
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
* memory (e.g. `Load`, `Store`, `InitializeParameter`, `IndirectReadSideEffect`).
|
||||
*/
|
||||
class AddressOperandTag extends OperandTag, TAddressOperand {
|
||||
override final string toString() {
|
||||
@@ -63,6 +67,37 @@ AddressOperandTag addressOperand() {
|
||||
result = TAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends OperandTag, TBufferSizeOperand {
|
||||
override final string toString() {
|
||||
result = "BufferSize"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||
*/
|
||||
class SideEffectOperandTag extends OperandTag, TSideEffectOperand {
|
||||
override final string toString() {
|
||||
result = "SideEffect"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 2
|
||||
}
|
||||
}
|
||||
|
||||
SideEffectOperandTag sideEffectOperand() {
|
||||
result = TSideEffectOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
@@ -73,7 +108,7 @@ class CopySourceOperandTag extends OperandTag, TCopySourceOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 1
|
||||
result = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +125,7 @@ class UnaryOperandTag extends OperandTag, TUnaryOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 2
|
||||
result = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +142,7 @@ class LeftOperandTag extends OperandTag, TLeftOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 3
|
||||
result = 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +159,7 @@ class RightOperandTag extends OperandTag, TRightOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 4
|
||||
result = 6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +176,7 @@ class ReturnValueOperandTag extends OperandTag, TReturnValueOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 5
|
||||
result = 7
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +193,7 @@ class ExceptionOperandTag extends OperandTag, TExceptionOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 6
|
||||
result = 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +210,7 @@ class ConditionOperandTag extends OperandTag, TConditionOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 7
|
||||
result = 9
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +228,7 @@ class UnmodeledUseOperandTag extends OperandTag, TUnmodeledUseOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 8
|
||||
result = 10
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +245,7 @@ class CallTargetOperandTag extends OperandTag, TCallTargetOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 9
|
||||
result = 11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +275,7 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand {
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 10
|
||||
result = 12
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
@@ -268,7 +303,7 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag,
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + argIndex
|
||||
result = 14 + argIndex
|
||||
}
|
||||
|
||||
final int getArgIndex() {
|
||||
@@ -279,3 +314,31 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag,
|
||||
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
class ChiTotalOperandTag extends OperandTag, TChiTotalOperand {
|
||||
override final string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 14
|
||||
}
|
||||
}
|
||||
|
||||
ChiTotalOperandTag chiTotalOperand() {
|
||||
result = TChiTotalOperand()
|
||||
}
|
||||
|
||||
class ChiPartialOperandTag extends OperandTag, TChiPartialOperand {
|
||||
override final string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 15
|
||||
}
|
||||
}
|
||||
|
||||
ChiPartialOperandTag chiPartialOperand() {
|
||||
result = TChiPartialOperand()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
private import implementations.Memcpy
|
||||
private import implementations.Printf
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::identity`
|
||||
*/
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
(
|
||||
this.getName() = "move" or
|
||||
this.getName() = "forward"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate neverReadsMemory() {
|
||||
any()
|
||||
}
|
||||
|
||||
override predicate neverWritesMemory() {
|
||||
any()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
// These functions simply return the argument value.
|
||||
index = 0
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
// These functions simply return the argument value.
|
||||
index = 0
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isInParameter(0) and output.isOutReturnValue()
|
||||
}
|
||||
}
|
||||
54
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
54
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate alias modeling of library
|
||||
* functions when source code is not available. To use this QL library,
|
||||
* create a QL class extending `AliasFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by `AliasFunction`
|
||||
* to match the flow within that function.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Models the aliasing behavior of a library function.
|
||||
*/
|
||||
abstract class AliasFunction extends Function {
|
||||
/**
|
||||
* Holds if the address passed to the parameter at the specified index is never retained after
|
||||
* the function returns.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int* g;
|
||||
* int* func(int* p, int* q, int* r, int* s, int n) {
|
||||
* *s = 1; // `s` does not escape.
|
||||
* g = p; // Stored in global. `p` escapes.
|
||||
* if (rand()) {
|
||||
* return q; // `q` escapes via the return value.
|
||||
* }
|
||||
* else {
|
||||
* return r + n; // `r` escapes via the return value, even though an offset has been added.
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For the above function, the following terms hold:
|
||||
* - `parameterEscapesOnlyViaReturn(1)`
|
||||
* - `parameterEscapesOnlyViaReturn(2)`
|
||||
* - `parameterNeverEscapes(3)`
|
||||
*/
|
||||
abstract predicate parameterNeverEscapes(int index);
|
||||
|
||||
/**
|
||||
* Holds if the address passed to the parameter at the specified index escapes via the return
|
||||
* value of the function, but does not otherwise escape. See the comment for
|
||||
* `parameterNeverEscapes` for an example.
|
||||
*/
|
||||
abstract predicate parameterEscapesOnlyViaReturn(int index);
|
||||
|
||||
/**
|
||||
* Holds if the function always returns the value of the parameter at the specified index.
|
||||
*/
|
||||
abstract predicate parameterIsAlwaysReturned(int index);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user