mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #474 from rdmarsh2/rdmarsh/cpp/call-side-effect
C++: Initital aliased SSA with Chi nodes and function side effects
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import cpp
|
||||
|
||||
newtype TMemoryAccessKind =
|
||||
private newtype TMemoryAccessKind =
|
||||
TIndirectMemoryAccess() or
|
||||
TIndirectMayMemoryAccess() or
|
||||
TBufferMemoryAccess() or
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess()
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
TChiPartialMemoryAccess()
|
||||
|
||||
/**
|
||||
* Describes the set of memory locations memory accessed by a memory operand or
|
||||
@@ -15,8 +20,8 @@ class MemoryAccessKind extends TMemoryAccessKind {
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory at the address specified by the
|
||||
* `AddressOperand` on the same instruction.
|
||||
* The operand or result accesses memory at the address specified by the `AddressOperand` on the
|
||||
* same instruction.
|
||||
*/
|
||||
class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
override string toString() {
|
||||
@@ -24,6 +29,38 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory at the address specified by the
|
||||
* `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "indirect(may)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory starting at the address specified by the `AddressOperand`
|
||||
* on the same instruction, accessing a number of consecutive elements given by the
|
||||
* `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
|
||||
override string toString() {
|
||||
result = "buffer"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory starting at the address
|
||||
* specified by the `AddressOperand` on the same instruction, accessing a number of consecutive
|
||||
* elements given by the `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "buffer(may)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses all memory whose address has escaped.
|
||||
*/
|
||||
@@ -43,6 +80,26 @@ class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiTotal operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
|
||||
override string toString() {
|
||||
result = "chi(total)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiPartial operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
|
||||
override string toString() {
|
||||
result = "chi(partial)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand accesses memory not modeled in SSA. Used only on the result of
|
||||
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.
|
||||
|
||||
@@ -54,11 +54,21 @@ 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()
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() {
|
||||
@@ -92,6 +102,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 +181,19 @@ 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" } }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
|
||||
(
|
||||
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -162,9 +169,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 +181,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"
|
||||
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that throws an exception.
|
||||
*/
|
||||
@@ -1195,6 +1322,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 +1345,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 +1365,73 @@ 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 a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
@@ -358,3 +390,38 @@ class MemoryOperand extends Operand {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends Operand {
|
||||
ChiTotalOperand() {
|
||||
this = TNonPhiOperand(_, chiTotalOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiTotalMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends Operand {
|
||||
ChiPartialOperand() {
|
||||
this = TNonPhiOperand(_, chiPartialOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiPartialMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
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
|
||||
}
|
||||
|
||||
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(VirtualIRVariable vvar, IntValue offset, IntValue size) {
|
||||
exists(Instruction instr |
|
||||
exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), vvar.getIRVariable(), offset) and
|
||||
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.getVirtualVariable() = getVirtualVariable(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 {
|
||||
VirtualIRVariable vvar;
|
||||
IntValue offset;
|
||||
IntValue size;
|
||||
|
||||
VariableMemoryAccess() {
|
||||
this = TVariableMemoryAccess(vvar, offset, size)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = vvar.toString()
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
|
||||
IntValue getOffset() {
|
||||
result = offset
|
||||
}
|
||||
|
||||
IntValue getSize() {
|
||||
result = size
|
||||
}
|
||||
|
||||
final override predicate isPartialMemoryAccess() {
|
||||
getOffset() != 0
|
||||
or
|
||||
getSize() != vvar.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 |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and
|
||||
result = getVariableMemoryAccess(var, i, operand.getDefinitionInstruction().getResultSize())
|
||||
)
|
||||
else (
|
||||
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR())) and
|
||||
not operand.getInstruction() instanceof UnmodeledUseInstruction
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstruction as Construction
|
||||
private import NewIR
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
|
||||
|
||||
private OldIR::IRBlock getOldBlock(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getInstruction(index)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = getOldBlock(block).getInstructionCount()
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
|
||||
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
|
||||
}
|
||||
|
||||
cached Instruction getFirstInstruction(TIRBlock block) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getFirstInstruction()
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
@@ -18,6 +17,10 @@ cached private module Cached {
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
} or
|
||||
ChiTag(OldIR::Instruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
}
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
@@ -40,12 +43,28 @@ cached private module Cached {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
result.getFunction() = var.getFunction() and
|
||||
@@ -92,6 +111,15 @@ cached private module Cached {
|
||||
tag = PhiTag(vvar, block) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::Instruction 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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,11 +153,11 @@ cached private module Cached {
|
||||
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()
|
||||
)
|
||||
@@ -146,7 +174,17 @@ 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,
|
||||
@@ -160,12 +198,27 @@ cached private module Cached {
|
||||
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, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
|
||||
int defRank, int defIndex, OldIR::IRBlock 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 |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
@@ -181,8 +234,24 @@ 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 (
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached IRVariable getInstructionVariable(Instruction instruction) {
|
||||
@@ -228,6 +297,18 @@ 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) {
|
||||
block.getInstruction(index) = instr and
|
||||
@@ -253,7 +334,16 @@ cached private module Cached {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock 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()
|
||||
)
|
||||
@@ -384,6 +474,15 @@ cached private module Cached {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
ma.getVirtualVariable() = vvar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import SimpleSSA as Alias
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import SimpleSSAInternal
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
private newtype TVirtualVariable =
|
||||
MkVirtualVariable(IRVariable var) {
|
||||
not variableAddressEscapes(var)
|
||||
}
|
||||
|
||||
private VirtualVariable getVirtualVariable(IRVariable var) {
|
||||
result.getIRVariable() = var
|
||||
}
|
||||
|
||||
class VirtualVariable extends TVirtualVariable {
|
||||
IRVariable var;
|
||||
|
||||
VirtualVariable() {
|
||||
this = MkVirtualVariable(var)
|
||||
}
|
||||
|
||||
final string toString() {
|
||||
result = var.toString()
|
||||
}
|
||||
|
||||
final IRVariable getIRVariable() {
|
||||
result = var
|
||||
}
|
||||
|
||||
// REVIEW: This should just be on MemoryAccess
|
||||
final Type getType() {
|
||||
result = var.getType()
|
||||
}
|
||||
|
||||
final string getUniqueId() {
|
||||
result = var.getUniqueId()
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TMemoryAccess =
|
||||
MkMemoryAccess(VirtualVariable vvar)
|
||||
|
||||
private MemoryAccess getMemoryAccess(IRVariable var) {
|
||||
result.getVirtualVariable() = getVirtualVariable(var)
|
||||
}
|
||||
|
||||
class MemoryAccess extends TMemoryAccess {
|
||||
VirtualVariable vvar;
|
||||
|
||||
MemoryAccess() {
|
||||
this = MkMemoryAccess(vvar)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = vvar.toString()
|
||||
}
|
||||
|
||||
VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
result instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
exists(IRVariable var |
|
||||
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
|
||||
var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
exists(IRVariable var |
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import AliasAnalysis as Alias
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
|
||||
(
|
||||
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -162,9 +169,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 +181,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"
|
||||
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that throws an exception.
|
||||
*/
|
||||
@@ -1195,6 +1322,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 +1345,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 +1365,73 @@ 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 a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
@@ -358,3 +390,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.Instruction
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
@@ -168,6 +167,13 @@ cached private module Cached {
|
||||
result = element.getInstructionResultSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
@@ -40,9 +40,11 @@ newtype TInstructionTag =
|
||||
ExitFunctionTag() or
|
||||
UnmodeledDefinitionTag() or
|
||||
UnmodeledUseTag() or
|
||||
AliasedDefinitionTag() or
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
CallSideEffectTag() or
|
||||
AllocationSizeTag() or
|
||||
AllocationElementSizeTag() or
|
||||
AllocationExtentConvertTag() or
|
||||
@@ -61,6 +63,7 @@ newtype TInstructionTag =
|
||||
CatchTag() or
|
||||
ThrowTag() or
|
||||
UnwindTag() or
|
||||
InitializerUninitializedTag() or
|
||||
InitializerFieldAddressTag(Field field) {
|
||||
fieldIsInitialized(field)
|
||||
} or
|
||||
@@ -91,6 +94,7 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load)
|
||||
tag = InitializerVariableAddressTag() and result = "InitVarAddr" or
|
||||
tag = InitializerStoreTag() and result = "InitStore" or
|
||||
tag = InitializerUninitializedTag() and result = "InitUninit" or
|
||||
tag = ZeroPadStringConstantTag() and result = "ZeroPadConst" or
|
||||
tag = ZeroPadStringElementIndexTag() and result = "ZeroPadElemIndex" or
|
||||
tag = ZeroPadStringElementAddressTag() and result = "ZeroPadElemAddr" or
|
||||
@@ -110,9 +114,11 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = ExitFunctionTag() and result = "ExitFunc" or
|
||||
tag = UnmodeledDefinitionTag() and result = "UnmodeledDef" or
|
||||
tag = UnmodeledUseTag() and result = "UnmodeledUse" or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef" or
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch" or
|
||||
tag = CallTargetTag() and result = "CallTarget" or
|
||||
tag = CallTag() and result = "Call" or
|
||||
tag = CallSideEffectTag() and result = "CallSideEffect" or
|
||||
tag = AllocationSizeTag() and result = "AllocSize" or
|
||||
tag = AllocationElementSizeTag() and result = "AllocElemSize" or
|
||||
tag = AllocationExtentConvertTag() and result = "AllocExtConv" or
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
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
|
||||
(
|
||||
tag = CallSideEffectTag() and
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType 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 = getInstruction(CallSideEffectTag())
|
||||
) or
|
||||
(
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,6 +534,14 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
result = getParent().getExceptionSuccessorInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary instruction for the side effect instruction that was
|
||||
* generated by this element for tag `tag`.
|
||||
*/
|
||||
Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element generates a temporary variable with type `type`.
|
||||
* `tag` must be unique for each variable generated from the same AST node
|
||||
|
||||
@@ -8,6 +8,7 @@ private import TranslatedDeclarationEntry
|
||||
private import TranslatedElement
|
||||
private import TranslatedFunction
|
||||
private import TranslatedInitialization
|
||||
import TranslatedCall
|
||||
|
||||
/**
|
||||
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
|
||||
@@ -1769,163 +1770,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
* call in the source code, or could be a call that is part of the translation
|
||||
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
|
||||
*/
|
||||
abstract class TranslatedCall extends TranslatedExpr {
|
||||
override final TranslatedElement getChild(int id) {
|
||||
// We choose the child's id in the order of evaluation.
|
||||
// The qualifier is evaluated before the call target, because the value of
|
||||
// the call target may depend on the value of the qualifier for virtual
|
||||
// calls.
|
||||
id = -2 and result = getQualifier() or
|
||||
id = -1 and result = getCallTarget() or
|
||||
result = getArgument(id)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(getQualifier()) then
|
||||
result = getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getCallResultType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getQualifier() and
|
||||
result = getFirstCallTargetInstruction()
|
||||
) or
|
||||
(
|
||||
child = getCallTarget() and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
) or
|
||||
exists(int argIndex |
|
||||
child = getArgument(argIndex) and
|
||||
if exists(getArgument(argIndex + 1)) then
|
||||
result = getArgument(argIndex + 1).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
tag = CallTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = CallTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = getCallTargetResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = getQualifierResult()
|
||||
) or
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result type of the call.
|
||||
*/
|
||||
abstract Type getCallResultType();
|
||||
|
||||
/**
|
||||
* Holds if the call has a `this` argument.
|
||||
*/
|
||||
predicate hasQualifier() {
|
||||
exists(getQualifier())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
|
||||
*/
|
||||
TranslatedExpr getCallTarget() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first instruction of the sequence to evaluate the call target.
|
||||
* By default, this is just the first instruction of `getCallTarget()`, but
|
||||
* it can be overridden by a subclass for cases where there is a call target
|
||||
* that is not computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getFirstCallTargetInstruction() {
|
||||
result = getCallTarget().getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the target of the call. By
|
||||
* default, this is just the result of `getCallTarget()`, but it can be
|
||||
* overridden by a subclass for cases where there is a call target that is not
|
||||
* computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getCallTargetResult() {
|
||||
result = getCallTarget().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
|
||||
* that is passed as the `this` argument.
|
||||
*/
|
||||
abstract TranslatedExpr getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
* By default, this is just the result of `getQualifier()`, but it can be
|
||||
* overridden by a subclass for cases where there is a `this` argument that is
|
||||
* not computed from a child expression (e.g. a constructor call).
|
||||
*/
|
||||
Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument.
|
||||
*/
|
||||
abstract TranslatedExpr getArgument(int index);
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
* argument. Otherwise, returns the call instruction.
|
||||
*/
|
||||
final Instruction getFirstArgumentOrCallInstruction() {
|
||||
if hasArguments() then
|
||||
result = getArgument(0).getFirstInstruction()
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the allocation size argument passed to `operator new`
|
||||
* in a `new` expression.
|
||||
@@ -2089,45 +1933,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a direct call to a specific function. Used for both
|
||||
* explicit calls (`TranslatedFunctionCall`) and implicit calls
|
||||
* (`TranslatedAllocatorCall`).
|
||||
*/
|
||||
abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
override final Instruction getFirstCallTargetInstruction() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override final Instruction getCallTargetResult() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// The database does not contain a `FunctionType` for a function unless
|
||||
// its address was taken, so we'll just use glval<Unknown> instead of
|
||||
// glval<FunctionType>.
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to `operator new` as part of a `new` or `new[]`
|
||||
* expression.
|
||||
@@ -2185,65 +1990,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall,
|
||||
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
|
||||
result.getAST() = newExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function.
|
||||
*/
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
|
||||
TranslatedCall {
|
||||
Call call;
|
||||
|
||||
TranslatedCallExpr() {
|
||||
expr = call
|
||||
}
|
||||
|
||||
override final Type getCallResultType() {
|
||||
result = getResultType()
|
||||
}
|
||||
|
||||
override final predicate hasArguments() {
|
||||
exists(call.getArgument(0))
|
||||
}
|
||||
|
||||
override final TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
override final TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call through a function pointer.
|
||||
*/
|
||||
class TranslatedExprCall extends TranslatedCallExpr {
|
||||
ExprCall exprCall;
|
||||
|
||||
TranslatedExprCall() {
|
||||
expr = exprCall
|
||||
}
|
||||
|
||||
override TranslatedExpr getCallTarget() {
|
||||
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a direct function call.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
FunctionCall funcCall;
|
||||
|
||||
TranslatedFunctionCall() {
|
||||
expr = funcCall
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class implemented by any `TranslatedElement` that has a child
|
||||
* expression that is a call to a constructor or destructor, in order to
|
||||
@@ -2257,27 +2003,6 @@ abstract class StructorCallContext extends TranslatedElement {
|
||||
abstract Instruction getReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call to a constructor.
|
||||
*/
|
||||
class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
TranslatedStructorCall() {
|
||||
funcCall instanceof ConstructorCall or
|
||||
funcCall instanceof DestructorCall
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
exists(StructorCallContext context |
|
||||
context = getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasQualifier() {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the destruction of a field from within
|
||||
* the destructor of the field's declaring class.
|
||||
|
||||
@@ -76,6 +76,9 @@ class TranslatedFunction extends TranslatedElement,
|
||||
(
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
result = getInstruction(AliasedDefinitionTag())
|
||||
) or (
|
||||
tag = AliasedDefinitionTag() and
|
||||
result = getInstruction(UnmodeledDefinitionTag())
|
||||
) or
|
||||
(
|
||||
@@ -153,6 +156,12 @@ class TranslatedFunction extends TranslatedElement,
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
@@ -218,6 +227,11 @@ class TranslatedFunction extends TranslatedElement,
|
||||
result.getFunction() = func and
|
||||
result.hasMemoryResult()
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
) or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
not getReturnType() instanceof VoidType and
|
||||
|
||||
@@ -148,7 +148,11 @@ class TranslatedArrayListInitialization extends
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
result = getTranslatedElementInitialization(initList, id)
|
||||
// The children are in initialization order
|
||||
result = rank[id + 1](TranslatedElementInitialization init |
|
||||
init.getInitList() = initList |
|
||||
init order by init.getElementIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,15 +672,6 @@ class TranslatedFieldValueInitialization extends
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedElementInitialization` for element `elementIndex` in
|
||||
* initializer list `initList`.
|
||||
*/
|
||||
TranslatedElementInitialization getTranslatedElementInitialization(
|
||||
ArrayAggregateLiteral initList, int elementIndex) {
|
||||
result.getInitList() = initList and result.getElementIndex() = elementIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an element of an initializer list.
|
||||
@@ -717,7 +712,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
tag = getElementIndexTag() and
|
||||
result = getInstruction(getElementAddressTag()) and
|
||||
kind instanceof GotoEdge
|
||||
@@ -767,9 +762,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an explicit element in an initializer list.
|
||||
*/
|
||||
class TranslatedExplicitElementInitialization extends
|
||||
TranslatedElementInitialization, TTranslatedExplicitElementInitialization,
|
||||
InitializationContext {
|
||||
class TranslatedExplicitElementInitialization extends TranslatedElementInitialization,
|
||||
TTranslatedExplicitElementInitialization, InitializationContext {
|
||||
int elementIndex;
|
||||
|
||||
TranslatedExplicitElementInitialization() {
|
||||
@@ -785,7 +779,7 @@ class TranslatedExplicitElementInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = getElementAddressTag() and
|
||||
@@ -816,8 +810,8 @@ class TranslatedExplicitElementInitialization extends
|
||||
* Represents the IR translation of the initialization of a range of array
|
||||
* elements without corresponding elements in the initializer list.
|
||||
*/
|
||||
class TranslatedElementValueInitialization extends
|
||||
TranslatedElementInitialization, TTranslatedElementValueInitialization {
|
||||
class TranslatedElementValueInitialization extends TranslatedElementInitialization,
|
||||
TTranslatedElementValueInitialization {
|
||||
int elementIndex;
|
||||
int elementCount;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
private import Construction::BlockConstruction
|
||||
import Cached
|
||||
|
||||
class IRBlock extends TIRBlock {
|
||||
final string toString() {
|
||||
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) // Incoming edge is not a GotoEdge
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** Gets the index of `i` in its `IRBlock`. */
|
||||
private int getMemberIndex(Instruction i) {
|
||||
startsBasicBlock(i) and
|
||||
result = 0
|
||||
or
|
||||
exists(Instruction iPrev |
|
||||
adjacentInBlock(iPrev, i) and
|
||||
result = getMemberIndex(iPrev) + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
exists(Instruction first |
|
||||
block = MkIRBlock(first) and
|
||||
index = getMemberIndex(result) and
|
||||
adjacentInBlock*(first, result)
|
||||
)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = strictcount(getInstruction(block, _))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,18 @@ module InstructionSanity {
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
|
||||
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
|
||||
(
|
||||
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -162,9 +169,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 +181,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"
|
||||
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that calls a function.
|
||||
*/
|
||||
class CallInstruction extends Instruction {
|
||||
CallInstruction() {
|
||||
opcode instanceof Opcode::Call
|
||||
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that throws an exception.
|
||||
*/
|
||||
@@ -1195,6 +1322,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 +1345,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 +1365,73 @@ 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 a built-in operation. This is used to represent
|
||||
* operations such as access to variable argument lists.
|
||||
|
||||
@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
@@ -358,3 +390,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstruction as Construction
|
||||
private import NewIR
|
||||
|
||||
import Cached
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
|
||||
|
||||
private OldIR::IRBlock getOldBlock(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
|
||||
cached Instruction getInstruction(TIRBlock block, int index) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getInstruction(index)
|
||||
}
|
||||
|
||||
cached int getInstructionCount(TIRBlock block) {
|
||||
result = getOldBlock(block).getInstructionCount()
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
|
||||
}
|
||||
|
||||
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
|
||||
blockSuccessor(pred, succ, _)
|
||||
}
|
||||
|
||||
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
|
||||
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
|
||||
}
|
||||
|
||||
cached Instruction getFirstInstruction(TIRBlock block) {
|
||||
Construction::getOldInstruction(result) =
|
||||
getOldBlock(block).getFirstInstruction()
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
@@ -18,6 +17,10 @@ cached private module Cached {
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
} or
|
||||
ChiTag(OldIR::Instruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
}
|
||||
|
||||
cached class InstructionTagType extends TInstructionTag {
|
||||
@@ -40,12 +43,28 @@ cached private module Cached {
|
||||
getOldInstruction(result) = instr
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
|
||||
* corresponding to `instr` if there is no `Chi` node.
|
||||
*/
|
||||
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
|
||||
result = getChiInstruction(instr)
|
||||
or
|
||||
not exists(getChiInstruction(instr)) and
|
||||
result = getNewInstruction(instr)
|
||||
}
|
||||
|
||||
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
|
||||
Alias::VirtualVariable vvar) {
|
||||
result.getFunction() = func and
|
||||
result.getAST() = oldBlock.getFirstInstruction().getAST() and
|
||||
result.getTag() = PhiTag(vvar, oldBlock)
|
||||
}
|
||||
|
||||
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
|
||||
hasChiNode(_, instr) and
|
||||
result.getTag() = ChiTag(instr)
|
||||
}
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
result.getFunction() = var.getFunction() and
|
||||
@@ -92,6 +111,15 @@ cached private module Cached {
|
||||
tag = PhiTag(vvar, block) and
|
||||
resultType = vvar.getType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
exists(OldIR::Instruction 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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,11 +153,11 @@ cached private module Cached {
|
||||
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()
|
||||
)
|
||||
@@ -146,7 +174,17 @@ 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,
|
||||
@@ -160,12 +198,27 @@ cached private module Cached {
|
||||
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, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
|
||||
int defRank, int defIndex, OldIR::IRBlock 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 |
|
||||
instr.getTag() = PhiTag(_, oldBlock) and
|
||||
@@ -181,8 +234,24 @@ 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 (
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = getChiInstruction(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached IRVariable getInstructionVariable(Instruction instruction) {
|
||||
@@ -228,6 +297,18 @@ 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) {
|
||||
block.getInstruction(index) = instr and
|
||||
@@ -253,7 +334,16 @@ cached private module Cached {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock 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()
|
||||
)
|
||||
@@ -384,6 +474,15 @@ cached private module Cached {
|
||||
hasFrontierPhiNode(vvar, phiBlock)
|
||||
//or ssa_sanitized_custom_phi_node(vvar, block)
|
||||
}
|
||||
|
||||
private predicate hasChiNode(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction def) {
|
||||
exists(Alias::MemoryAccess ma |
|
||||
ma = Alias::getResultMemoryAccess(def) and
|
||||
ma.isPartialMemoryAccess() and
|
||||
ma.getVirtualVariable() = vvar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import SimpleSSAInternal
|
||||
import AliasAnalysis
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
@@ -60,11 +59,17 @@ class MemoryAccess extends TMemoryAccess {
|
||||
VirtualVariable getVirtualVariable() {
|
||||
result = vvar
|
||||
}
|
||||
|
||||
predicate isPartialMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
none() // Avoid compiler error in SSAConstruction
|
||||
}
|
||||
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
import AliasAnalysis as Alias
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as InputIR
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -193,6 +193,13 @@ private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck)
|
||||
|
||||
/** Holds if the sign of `e` is too complicated to determine. */
|
||||
private predicate unknownSign(Instruction i) {
|
||||
// REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than
|
||||
// the ones we don't understand. Currently, if we try to compute the sign of an instruction that
|
||||
// we don't understand, and it isn't on this list, we incorrectly compute the sign as "none"
|
||||
// instead of "+,0,-".
|
||||
// Even better, we could track the state of each instruction as a power set of {non-negative,
|
||||
// non-positive, non-zero}, which would mean that the representation of the sign of an unknown
|
||||
// value would be the empty set.
|
||||
(
|
||||
i instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
@@ -203,6 +210,8 @@ private predicate unknownSign(Instruction i) {
|
||||
i instanceof BuiltInInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
i instanceof ChiInstruction
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3325,10 +3325,10 @@ ir.cpp:
|
||||
# 504| expr: {...}
|
||||
# 504| Type = Point
|
||||
# 504| ValueCategory = prvalue
|
||||
# 504| 0: x
|
||||
# 504| .x: x
|
||||
# 504| Type = int
|
||||
# 504| ValueCategory = prvalue(load)
|
||||
# 504| 1: (int)...
|
||||
# 504| .y: (int)...
|
||||
# 504| Conversion = floating point to integral conversion
|
||||
# 504| Type = int
|
||||
# 504| ValueCategory = prvalue
|
||||
@@ -3342,7 +3342,7 @@ ir.cpp:
|
||||
# 505| expr: {...}
|
||||
# 505| Type = Point
|
||||
# 505| ValueCategory = prvalue
|
||||
# 505| 0: x
|
||||
# 505| .x: x
|
||||
# 505| Type = int
|
||||
# 505| ValueCategory = prvalue(load)
|
||||
# 506| 2: declaration
|
||||
@@ -3390,13 +3390,13 @@ ir.cpp:
|
||||
# 514| expr: {...}
|
||||
# 514| Type = Rect
|
||||
# 514| ValueCategory = prvalue
|
||||
# 514| 0: {...}
|
||||
# 514| .topLeft: {...}
|
||||
# 514| Type = Point
|
||||
# 514| ValueCategory = prvalue
|
||||
# 514| 0: x
|
||||
# 514| .x: x
|
||||
# 514| Type = int
|
||||
# 514| ValueCategory = prvalue(load)
|
||||
# 514| 1: (int)...
|
||||
# 514| .y: (int)...
|
||||
# 514| Conversion = floating point to integral conversion
|
||||
# 514| Type = int
|
||||
# 514| ValueCategory = prvalue
|
||||
@@ -3410,26 +3410,26 @@ ir.cpp:
|
||||
# 515| expr: {...}
|
||||
# 515| Type = Rect
|
||||
# 515| ValueCategory = prvalue
|
||||
# 515| 0: {...}
|
||||
# 515| .topLeft: {...}
|
||||
# 515| Type = Point
|
||||
# 515| ValueCategory = prvalue
|
||||
# 515| 0: x
|
||||
# 515| .x: x
|
||||
# 515| Type = int
|
||||
# 515| ValueCategory = prvalue(load)
|
||||
# 515| 1: (int)...
|
||||
# 515| .y: (int)...
|
||||
# 515| Conversion = floating point to integral conversion
|
||||
# 515| Type = int
|
||||
# 515| ValueCategory = prvalue
|
||||
# 515| expr: f
|
||||
# 515| Type = float
|
||||
# 515| ValueCategory = prvalue(load)
|
||||
# 515| 1: {...}
|
||||
# 515| .bottomRight: {...}
|
||||
# 515| Type = Point
|
||||
# 515| ValueCategory = prvalue
|
||||
# 515| 0: x
|
||||
# 515| .x: x
|
||||
# 515| Type = int
|
||||
# 515| ValueCategory = prvalue(load)
|
||||
# 515| 1: (int)...
|
||||
# 515| .y: (int)...
|
||||
# 515| Conversion = floating point to integral conversion
|
||||
# 515| Type = int
|
||||
# 515| ValueCategory = prvalue
|
||||
@@ -3443,16 +3443,16 @@ ir.cpp:
|
||||
# 516| expr: {...}
|
||||
# 516| Type = Rect
|
||||
# 516| ValueCategory = prvalue
|
||||
# 516| 0: {...}
|
||||
# 516| .topLeft: {...}
|
||||
# 516| Type = Point
|
||||
# 516| ValueCategory = prvalue
|
||||
# 516| 0: x
|
||||
# 516| .x: x
|
||||
# 516| Type = int
|
||||
# 516| ValueCategory = prvalue(load)
|
||||
# 516| 1: {...}
|
||||
# 516| .bottomRight: {...}
|
||||
# 516| Type = Point
|
||||
# 516| ValueCategory = prvalue
|
||||
# 516| 0: x
|
||||
# 516| .x: x
|
||||
# 516| Type = int
|
||||
# 516| ValueCategory = prvalue(load)
|
||||
# 517| 4: return ...
|
||||
@@ -3477,17 +3477,17 @@ ir.cpp:
|
||||
# 521| expr: {...}
|
||||
# 521| Type = int[3]
|
||||
# 521| ValueCategory = prvalue
|
||||
# 521| 0: x
|
||||
# 521| [0]: x
|
||||
# 521| Type = int
|
||||
# 521| ValueCategory = prvalue(load)
|
||||
# 521| 1: (int)...
|
||||
# 521| [1]: (int)...
|
||||
# 521| Conversion = floating point to integral conversion
|
||||
# 521| Type = int
|
||||
# 521| ValueCategory = prvalue
|
||||
# 521| expr: f
|
||||
# 521| Type = float
|
||||
# 521| ValueCategory = prvalue(load)
|
||||
# 521| 2: 0
|
||||
# 521| [2]: 0
|
||||
# 521| Type = int
|
||||
# 521| Value = 0
|
||||
# 521| ValueCategory = prvalue
|
||||
@@ -3498,7 +3498,7 @@ ir.cpp:
|
||||
# 522| expr: {...}
|
||||
# 522| Type = int[3]
|
||||
# 522| ValueCategory = prvalue
|
||||
# 522| 0: x
|
||||
# 522| [0]: x
|
||||
# 522| Type = int
|
||||
# 522| ValueCategory = prvalue(load)
|
||||
# 523| 3: return ...
|
||||
@@ -3524,7 +3524,7 @@ ir.cpp:
|
||||
# 531| expr: {...}
|
||||
# 531| Type = U
|
||||
# 531| ValueCategory = prvalue
|
||||
# 531| 0: (double)...
|
||||
# 531| .d: (double)...
|
||||
# 531| Conversion = floating point conversion
|
||||
# 531| Type = double
|
||||
# 531| ValueCategory = prvalue
|
||||
@@ -3708,7 +3708,7 @@ ir.cpp:
|
||||
# 577| expr: {...}
|
||||
# 577| Type = char[2]
|
||||
# 577| ValueCategory = prvalue
|
||||
# 577| 0: (char)...
|
||||
# 577| [0]: (char)...
|
||||
# 577| Conversion = integral conversion
|
||||
# 577| Type = char
|
||||
# 577| Value = 0
|
||||
@@ -3724,7 +3724,7 @@ ir.cpp:
|
||||
# 578| expr: {...}
|
||||
# 578| Type = char[2]
|
||||
# 578| ValueCategory = prvalue
|
||||
# 578| 0: (char)...
|
||||
# 578| [0]: (char)...
|
||||
# 578| Conversion = integral conversion
|
||||
# 578| Type = char
|
||||
# 578| Value = 0
|
||||
@@ -3733,7 +3733,7 @@ ir.cpp:
|
||||
# 578| Type = int
|
||||
# 578| Value = 0
|
||||
# 578| ValueCategory = prvalue
|
||||
# 578| 1: (char)...
|
||||
# 578| [1]: (char)...
|
||||
# 578| Conversion = integral conversion
|
||||
# 578| Type = char
|
||||
# 578| Value = 1
|
||||
@@ -3749,7 +3749,7 @@ ir.cpp:
|
||||
# 579| expr: {...}
|
||||
# 579| Type = char[3]
|
||||
# 579| ValueCategory = prvalue
|
||||
# 579| 0: (char)...
|
||||
# 579| [0]: (char)...
|
||||
# 579| Conversion = integral conversion
|
||||
# 579| Type = char
|
||||
# 579| Value = 0
|
||||
@@ -6372,7 +6372,7 @@ ir.cpp:
|
||||
# 954| 1: {...}
|
||||
# 954| Type = String[]
|
||||
# 954| ValueCategory = prvalue
|
||||
# 954| 0: call to String
|
||||
# 954| [0]: call to String
|
||||
# 954| Type = void
|
||||
# 954| ValueCategory = prvalue
|
||||
# 954| 2: n
|
||||
@@ -6414,7 +6414,7 @@ ir.cpp:
|
||||
# 957| 1: {...}
|
||||
# 957| Type = DefaultCtorWithDefaultParam[]
|
||||
# 957| ValueCategory = prvalue
|
||||
# 957| 0: call to DefaultCtorWithDefaultParam
|
||||
# 957| [0]: call to DefaultCtorWithDefaultParam
|
||||
# 957| Type = void
|
||||
# 957| ValueCategory = prvalue
|
||||
# 957| 2: n
|
||||
@@ -6427,15 +6427,15 @@ ir.cpp:
|
||||
# 958| 1: {...}
|
||||
# 958| Type = int[3]
|
||||
# 958| ValueCategory = prvalue
|
||||
# 958| 0: 0
|
||||
# 958| [0]: 0
|
||||
# 958| Type = int
|
||||
# 958| Value = 0
|
||||
# 958| ValueCategory = prvalue
|
||||
# 958| 1: 1
|
||||
# 958| [1]: 1
|
||||
# 958| Type = int
|
||||
# 958| Value = 1
|
||||
# 958| ValueCategory = prvalue
|
||||
# 958| 2: 2
|
||||
# 958| [2]: 2
|
||||
# 958| Type = int
|
||||
# 958| Value = 2
|
||||
# 958| ValueCategory = prvalue
|
||||
@@ -6453,11 +6453,11 @@ ir.cpp:
|
||||
# 962| expr: {...}
|
||||
# 962| Type = int[1000]
|
||||
# 962| ValueCategory = prvalue
|
||||
# 962| 0: 10002
|
||||
# 962| [2]: 10002
|
||||
# 962| Type = int
|
||||
# 962| Value = 10002
|
||||
# 962| ValueCategory = prvalue
|
||||
# 962| 1: 10900
|
||||
# 962| [900]: 10900
|
||||
# 962| Type = int
|
||||
# 962| Value = 10900
|
||||
# 962| ValueCategory = prvalue
|
||||
@@ -6591,3 +6591,80 @@ ir.cpp:
|
||||
# 983| ValueCategory = prvalue(load)
|
||||
# 983| 1: { ... }
|
||||
# 985| 3: return ...
|
||||
# 1005| ChiPhiNode(Point *, bool, bool) -> int
|
||||
# 1005| params:
|
||||
# 1005| 0: p
|
||||
# 1005| Type = Point *
|
||||
# 1005| 1: which1
|
||||
# 1005| Type = bool
|
||||
# 1005| 2: which2
|
||||
# 1005| Type = bool
|
||||
# 1005| body: { ... }
|
||||
# 1006| 0: if (...) ...
|
||||
# 1006| 0: which1
|
||||
# 1006| Type = bool
|
||||
# 1006| ValueCategory = prvalue(load)
|
||||
# 1006| 1: { ... }
|
||||
# 1007| 0: ExprStmt
|
||||
# 1007| 0: ... ++
|
||||
# 1007| Type = int
|
||||
# 1007| ValueCategory = prvalue
|
||||
# 1007| 0: x
|
||||
# 1007| Type = int
|
||||
# 1007| ValueCategory = lvalue
|
||||
# 1007| -1: p
|
||||
# 1007| Type = Point *
|
||||
# 1007| ValueCategory = prvalue(load)
|
||||
# 1008| 2: { ... }
|
||||
# 1009| 0: ExprStmt
|
||||
# 1009| 0: ... ++
|
||||
# 1009| Type = int
|
||||
# 1009| ValueCategory = prvalue
|
||||
# 1009| 0: y
|
||||
# 1009| Type = int
|
||||
# 1009| ValueCategory = lvalue
|
||||
# 1009| -1: p
|
||||
# 1009| Type = Point *
|
||||
# 1009| ValueCategory = prvalue(load)
|
||||
# 1012| 1: if (...) ...
|
||||
# 1012| 0: which2
|
||||
# 1012| Type = bool
|
||||
# 1012| ValueCategory = prvalue(load)
|
||||
# 1012| 1: { ... }
|
||||
# 1013| 0: ExprStmt
|
||||
# 1013| 0: ... ++
|
||||
# 1013| Type = int
|
||||
# 1013| ValueCategory = prvalue
|
||||
# 1013| 0: x
|
||||
# 1013| Type = int
|
||||
# 1013| ValueCategory = lvalue
|
||||
# 1013| -1: p
|
||||
# 1013| Type = Point *
|
||||
# 1013| ValueCategory = prvalue(load)
|
||||
# 1014| 2: { ... }
|
||||
# 1015| 0: ExprStmt
|
||||
# 1015| 0: ... ++
|
||||
# 1015| Type = int
|
||||
# 1015| ValueCategory = prvalue
|
||||
# 1015| 0: y
|
||||
# 1015| Type = int
|
||||
# 1015| ValueCategory = lvalue
|
||||
# 1015| -1: p
|
||||
# 1015| Type = Point *
|
||||
# 1015| ValueCategory = prvalue(load)
|
||||
# 1018| 2: return ...
|
||||
# 1018| 0: ... + ...
|
||||
# 1018| Type = int
|
||||
# 1018| ValueCategory = prvalue
|
||||
# 1018| 0: x
|
||||
# 1018| Type = int
|
||||
# 1018| ValueCategory = prvalue(load)
|
||||
# 1018| -1: p
|
||||
# 1018| Type = Point *
|
||||
# 1018| ValueCategory = prvalue(load)
|
||||
# 1018| 1: y
|
||||
# 1018| Type = int
|
||||
# 1018| ValueCategory = prvalue(load)
|
||||
# 1018| -1: p
|
||||
# 1018| Type = Point *
|
||||
# 1018| ValueCategory = prvalue(load)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1002,4 +1002,20 @@ void OperatorDeleteArray() {
|
||||
}
|
||||
#endif
|
||||
|
||||
int ChiPhiNode(Point *p, bool which1, bool which2) {
|
||||
if (which1) {
|
||||
p->x++;
|
||||
} else {
|
||||
p->y++;
|
||||
}
|
||||
|
||||
if (which2) {
|
||||
p->x++;
|
||||
} else {
|
||||
p->y++;
|
||||
}
|
||||
|
||||
return p->x + p->y;
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
||||
| IR: CallNestedTemplateFunc | 1 |
|
||||
| IR: CallViaFuncPtr | 1 |
|
||||
| IR: CastToVoid | 1 |
|
||||
| IR: ChiPhiNode | 7 |
|
||||
| IR: Comma | 1 |
|
||||
| IR: CompoundAssignment | 1 |
|
||||
| IR: ConditionValues | 13 |
|
||||
@@ -97,7 +98,7 @@
|
||||
| IR: VirtualMemberFunction | 1 |
|
||||
| IR: WhileStatements | 4 |
|
||||
| IR: WhileStmtWithDeclaration | 8 |
|
||||
| IR: designatedInit | 4 |
|
||||
| IR: designatedInit | 1 |
|
||||
| IR: min | 4 |
|
||||
| IR: operator= | 1 |
|
||||
| IR: ~Base | 1 |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user