Merge remote-tracking branch 'upstream/next' into qlucie/master

This commit is contained in:
Max Schaefer
2019-01-04 10:35:57 +00:00
2357 changed files with 143173 additions and 20625 deletions

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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]"

View File

@@ -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) {

View File

@@ -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
}
}
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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)`
)
)
)
)

View File

@@ -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."
)
}

View File

@@ -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>

View File

@@ -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 + "."

View File

@@ -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.

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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)

View File

@@ -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. */

View File

@@ -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

View File

@@ -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))
}
}
/**

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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)" }
}

View File

@@ -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 }

View File

@@ -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 = "?" }

View 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
}

View 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)
}
}
}

View 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)
}
}
}

View 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)
}
}
}

View File

@@ -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)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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
}

View File

@@ -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()
}
}

View 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)
}

View File

@@ -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`.

View File

@@ -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" } }
}

View File

@@ -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)
}

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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()))
)
}

View File

@@ -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()
}
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR

View File

@@ -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
)
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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"
)
}

View File

@@ -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

View File

@@ -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)
)
}

View File

@@ -1,3 +0,0 @@
import AliasAnalysis as Alias
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR

View File

@@ -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)
}

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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()))
)
}

View File

@@ -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()
}
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.raw.IR as IR

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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())
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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;

View File

@@ -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)
)
}

View File

@@ -0,0 +1,7 @@
private import ReachableBlock as Reachability
private module ReachabilityGraph = Reachability::Graph;
module Graph {
import Reachability::Graph
class Block = Reachability::ReachableBlock;
}

View File

@@ -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()
)
)
}
}

View File

@@ -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"
)
)
}
}

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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()))
)
}

View File

@@ -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()
}
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR

View File

@@ -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()
}
}

View File

@@ -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"
)
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -1,2 +0,0 @@
import AliasAnalysis as Alias
import semmle.code.cpp.ir.implementation.raw.IR as InputIR

View File

@@ -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)
)
}

View File

@@ -0,0 +1,7 @@
private import ReachableBlock as Reachability
private module ReachabilityGraph = Reachability::Graph;
module Graph {
import Reachability::Graph
class Block = Reachability::ReachableBlock;
}

View File

@@ -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()
)
)
}
}

View File

@@ -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"
)
)
}
}

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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()
}

View File

@@ -1,3 +1,4 @@
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Memcpy
private import implementations.Printf

View File

@@ -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()
}
}

View 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