C++: insert Chi nodes in the IR successor relation

This commit adds Chi nodes to the successor relation and accounts for
them in the CFG, but does not add them to the SSA data graph. Chi nodes
are inserted for partial writes to any VirtualVariable, regardless of
whether the partial write reaches any uses.
This commit is contained in:
Robert Marsh
2018-11-02 12:52:21 -07:00
committed by Dave Bartolomeo
parent 1fb36ff7e7
commit a33b59103a
19 changed files with 1443 additions and 1263 deletions

View File

@@ -43,10 +43,6 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
],
"C++ SSA SimpleSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSA.qll"
],
"C++ SSA SSAConstruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"

View File

@@ -7,7 +7,9 @@ private newtype TMemoryAccessKind =
TBufferMayMemoryAccess() or
TEscapedMemoryAccess() or
TPhiMemoryAccess() or
TUnmodeledMemoryAccess()
TUnmodeledMemoryAccess() or
TChiOldMemoryAccess() or
TChiUpdateMemoryAccess()
/**
* Describes the set of memory locations memory accessed by a memory operand or
@@ -78,6 +80,25 @@ class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess {
}
}
/**
* The operand is a ChiOld operand, which accesses the same memory as its
* definition.
*/
class ChiOldMemoryAccess extends MemoryAccessKind, TChiOldMemoryAccess {
override string toString() {
result = "chi(old)"
}
}
/**
* The operand is a ChiUpdate operand, which accesses the same memory as its
* definition.
*/
class ChiUpdateMemoryAccess extends MemoryAccessKind, TChiUpdateMemoryAccess {
override string toString() {
result = "chi(updated)"
}
}
/**
* The operand accesses memory not modeled in SSA. Used only on the result of
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.

View File

@@ -66,7 +66,8 @@ private newtype TOpcode =
TIndirectMayWriteSideEffect() or
TBufferReadSideEffect() or
TBufferWriteSideEffect() or
TBufferMayWriteSideEffect()
TBufferMayWriteSideEffect() or
TChi()
class Opcode extends TOpcode {
string toString() {
@@ -192,4 +193,5 @@ module Opcode {
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
class Chi extends Opcode, TChi {override final string toString() { result = "Chi" } }
}

View File

@@ -1340,6 +1340,16 @@ class PhiInstruction extends Instruction {
}
}
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiUpdateMemoryAccess
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -0,0 +1,227 @@
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 |
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), vvar.getIRVariable(), offset) and
instr.getResultSize() = size
)
}
or
TUnknownMemoryAccess(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;
final override string toString() {
result = vvar.toString()
}
final override VirtualVariable getVirtualVariable() {
result = vvar
}
final override predicate isPartialMemoryAccess() {
any()
}
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 |
uvv = def.getVirtualVariable() and
uvv = use.getVirtualVariable() and
result instanceof MayPartiallyOverlap
)
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess 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()))
}
MemoryAccess getOperandMemoryAccess(Operand operand) {
operand.getMemoryAccess() instanceof IndirectMemoryAccess 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.getAddressOperand().getDefinitionInstruction().getResultSize())
)
else
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR()))
}

View File

@@ -17,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 {
@@ -39,12 +43,24 @@ cached private module Cached {
getOldInstruction(result) = instr
}
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
@@ -91,6 +107,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
)
}
@@ -181,7 +206,18 @@ cached private module Cached {
}
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) {
@@ -232,6 +268,11 @@ cached private module Cached {
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,
@@ -390,6 +431,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

View File

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

View File

@@ -1,84 +0,0 @@
import SimpleSSAInternal
import cpp
import Alias
private import InputIR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
private newtype TVirtualVariable =
MkVirtualVariable(IRVariable var) {
not variableAddressEscapes(var)
}
private VirtualVariable getVirtualVariable(IRVariable var) {
result.getIRVariable() = var
}
class VirtualVariable extends TVirtualVariable {
IRVariable var;
VirtualVariable() {
this = MkVirtualVariable(var)
}
final string toString() {
result = var.toString()
}
final IRVariable getIRVariable() {
result = var
}
// REVIEW: This should just be on MemoryAccess
final Type getType() {
result = var.getType()
}
final string getUniqueId() {
result = var.getUniqueId()
}
}
private newtype TMemoryAccess =
MkMemoryAccess(VirtualVariable vvar)
private MemoryAccess getMemoryAccess(IRVariable var) {
result.getVirtualVariable() = getVirtualVariable(var)
}
class MemoryAccess extends TMemoryAccess {
VirtualVariable vvar;
MemoryAccess() {
this = MkMemoryAccess(vvar)
}
string toString() {
result = vvar.toString()
}
VirtualVariable getVirtualVariable() {
result = vvar
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def.getVirtualVariable() = use.getVirtualVariable() and
result instanceof MustExactlyOverlap
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
exists(IRVariable var |
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
var, 0) and
result = getMemoryAccess(var)
)
}
MemoryAccess getOperandMemoryAccess(Operand operand) {
exists(IRVariable var |
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
result = getMemoryAccess(var)
)
}

View File

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

View File

@@ -1340,6 +1340,16 @@ class PhiInstruction extends Instruction {
}
}
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiUpdateMemoryAccess
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

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

View File

@@ -8,6 +8,7 @@ private import TranslatedDeclarationEntry
private import TranslatedElement
private import TranslatedFunction
private import TranslatedInitialization
import TranslatedCall
/**
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
@@ -1769,163 +1770,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
}
/**
* The IR translation of a call to a function. The call may be from an actual
* call in the source code, or could be a call that is part of the translation
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
*/
abstract class TranslatedCall extends TranslatedExpr {
override final TranslatedElement getChild(int id) {
// We choose the child's id in the order of evaluation.
// The qualifier is evaluated before the call target, because the value of
// the call target may depend on the value of the qualifier for virtual
// calls.
id = -2 and result = getQualifier() or
id = -1 and result = getCallTarget() or
result = getArgument(id)
}
override final Instruction getFirstInstruction() {
if exists(getQualifier()) then
result = getQualifier().getFirstInstruction()
else
result = getFirstCallTargetInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
tag = CallTag() and
opcode instanceof Opcode::Call and
resultType = getCallResultType() and
isGLValue = false
}
override Instruction getChildSuccessor(TranslatedElement child) {
(
child = getQualifier() and
result = getFirstCallTargetInstruction()
) or
(
child = getCallTarget() and
result = getFirstArgumentOrCallInstruction()
) or
exists(int argIndex |
child = getArgument(argIndex) and
if exists(getArgument(argIndex + 1)) then
result = getArgument(argIndex + 1).getFirstInstruction()
else
result = getInstruction(CallTag())
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
kind instanceof GotoEdge and
tag = CallTag() and
result = getParent().getChildSuccessor(this)
}
override Instruction getInstructionOperand(InstructionTag tag,
OperandTag operandTag) {
tag = CallTag() and
(
(
operandTag instanceof CallTargetOperandTag and
result = getCallTargetResult()
) or
(
operandTag instanceof ThisArgumentOperandTag and
result = getQualifierResult()
) or
exists(PositionalArgumentOperandTag argTag |
argTag = operandTag and
result = getArgument(argTag.getArgIndex()).getResult()
)
)
}
override final Instruction getResult() {
result = getInstruction(CallTag())
}
/**
* Gets the result type of the call.
*/
abstract Type getCallResultType();
/**
* Holds if the call has a `this` argument.
*/
predicate hasQualifier() {
exists(getQualifier())
}
/**
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
*/
TranslatedExpr getCallTarget() {
none()
}
/**
* Gets the first instruction of the sequence to evaluate the call target.
* By default, this is just the first instruction of `getCallTarget()`, but
* it can be overridden by a subclass for cases where there is a call target
* that is not computed from an expression (e.g. a direct call).
*/
Instruction getFirstCallTargetInstruction() {
result = getCallTarget().getFirstInstruction()
}
/**
* Gets the instruction whose result value is the target of the call. By
* default, this is just the result of `getCallTarget()`, but it can be
* overridden by a subclass for cases where there is a call target that is not
* computed from an expression (e.g. a direct call).
*/
Instruction getCallTargetResult() {
result = getCallTarget().getResult()
}
/**
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
* that is passed as the `this` argument.
*/
abstract TranslatedExpr getQualifier();
/**
* Gets the instruction whose result value is the `this` argument of the call.
* By default, this is just the result of `getQualifier()`, but it can be
* overridden by a subclass for cases where there is a `this` argument that is
* not computed from a child expression (e.g. a constructor call).
*/
Instruction getQualifierResult() {
result = getQualifier().getResult()
}
/**
* Gets the argument with the specified `index`. Does not include the `this`
* argument.
*/
abstract TranslatedExpr getArgument(int index);
/**
* If there are any arguments, gets the first instruction of the first
* argument. Otherwise, returns the call instruction.
*/
final Instruction getFirstArgumentOrCallInstruction() {
if hasArguments() then
result = getArgument(0).getFirstInstruction()
else
result = getInstruction(CallTag())
}
/**
* Holds if the call has any arguments, not counting the `this` argument.
*/
abstract predicate hasArguments();
}
/**
* The IR translation of the allocation size argument passed to `operator new`
* in a `new` expression.
@@ -2089,45 +1933,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
}
}
/**
* IR translation of a direct call to a specific function. Used for both
* explicit calls (`TranslatedFunctionCall`) and implicit calls
* (`TranslatedAllocatorCall`).
*/
abstract class TranslatedDirectCall extends TranslatedCall {
override final Instruction getFirstCallTargetInstruction() {
result = getInstruction(CallTargetTag())
}
override final Instruction getCallTargetResult() {
result = getInstruction(CallTargetTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
(
tag = CallTargetTag() and
opcode instanceof Opcode::FunctionAddress and
// The database does not contain a `FunctionType` for a function unless
// its address was taken, so we'll just use glval<Unknown> instead of
// glval<FunctionType>.
resultType instanceof UnknownType and
isGLValue = true
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
(
tag = CallTargetTag() and
kind instanceof GotoEdge and
result = getFirstArgumentOrCallInstruction()
)
}
}
/**
* The IR translation of a call to `operator new` as part of a `new` or `new[]`
* expression.
@@ -2185,65 +1990,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall,
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
result.getAST() = newExpr
}
/**
* The IR translation of a call to a function.
*/
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
TranslatedCall {
Call call;
TranslatedCallExpr() {
expr = call
}
override final Type getCallResultType() {
result = getResultType()
}
override final predicate hasArguments() {
exists(call.getArgument(0))
}
override final TranslatedExpr getQualifier() {
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
}
override final TranslatedExpr getArgument(int index) {
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
}
}
/**
* Represents the IR translation of a call through a function pointer.
*/
class TranslatedExprCall extends TranslatedCallExpr {
ExprCall exprCall;
TranslatedExprCall() {
expr = exprCall
}
override TranslatedExpr getCallTarget() {
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
}
}
/**
* Represents the IR translation of a direct function call.
*/
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
FunctionCall funcCall;
TranslatedFunctionCall() {
expr = funcCall
}
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = funcCall.getTarget()
}
}
/**
* Abstract class implemented by any `TranslatedElement` that has a child
* expression that is a call to a constructor or destructor, in order to
@@ -2257,27 +2003,6 @@ abstract class StructorCallContext extends TranslatedElement {
abstract Instruction getReceiver();
}
/**
* Represents the IR translation of a call to a constructor.
*/
class TranslatedStructorCall extends TranslatedFunctionCall {
TranslatedStructorCall() {
funcCall instanceof ConstructorCall or
funcCall instanceof DestructorCall
}
override Instruction getQualifierResult() {
exists(StructorCallContext context |
context = getParent() and
result = context.getReceiver()
)
}
override predicate hasQualifier() {
any()
}
}
/**
* Represents the IR translation of the destruction of a field from within
* the destructor of the field's declaring class.

View File

@@ -1340,6 +1340,16 @@ class PhiInstruction extends Instruction {
}
}
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiUpdateMemoryAccess
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -17,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 {
@@ -39,12 +43,24 @@ cached private module Cached {
getOldInstruction(result) = instr
}
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
@@ -91,6 +107,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
)
}
@@ -181,7 +206,18 @@ cached private module Cached {
}
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) {
@@ -232,6 +268,11 @@ cached private module Cached {
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,
@@ -390,6 +431,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

View File

@@ -1,7 +1,6 @@
import SimpleSSAInternal
import AliasAnalysis
import cpp
import Alias
private import InputIR
private import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
@@ -10,6 +9,7 @@ private newtype TVirtualVariable =
not variableAddressEscapes(var)
}
private VirtualVariable getVirtualVariable(IRVariable var) {
result.getIRVariable() = var
}
@@ -60,11 +60,17 @@ class MemoryAccess extends TMemoryAccess {
VirtualVariable getVirtualVariable() {
result = vvar
}
predicate isPartialMemoryAccess() {
none()
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def.getVirtualVariable() = use.getVirtualVariable() and
result instanceof MustExactlyOverlap
or
none() // Avoid compiler error in SSAConstruction
}
MemoryAccess getResultMemoryAccess(Instruction instr) {

View File

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

View File

@@ -27,7 +27,9 @@ private newtype TOperandTag =
exists(BuiltInOperation op |
exists(op.getChild(argIndex))
)
}
} or
TChiOldOperand() or
TChiUpdateOperand()
/**
* Identifies the kind of operand on an instruction. Each `Instruction` has at
@@ -311,4 +313,24 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag,
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
result = TPositionalArgumentOperand(argIndex)
}
}
class ChiOldOperand extends OperandTag, TChiOldOperand {
override final string toString() {
result = "ChiOld"
}
override final int getSortOrder() {
result = 14
}
}
class ChiUpdateOperand extends OperandTag, TChiUpdateOperand {
override final string toString() {
result = "ChiUpdate"
}
override final int getSortOrder() {
result = 15
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,12 @@
missingOperand
| ir.cpp:174:9:174:12 | Load: access to array | CopySource |
| ir.cpp:175:9:175:12 | Load: access to array | CopySource |
| ir.cpp:188:14:188:21 | Load: access to array | CopySource |
| ir.cpp:190:18:190:23 | Load: access to array | CopySource |
| ir.cpp:572:22:572:23 | Load: | CopySource |
| ir.cpp:676:12:676:12 | Load: (reference dereference) | CopySource |
| ir.cpp:894:14:894:43 | Load: __builtin_va_arg | CopySource |
| ir.cpp:895:30:895:33 | Load: __builtin_va_arg | CopySource |
unexpectedOperand
duplicateOperand
missingPhiOperand