From b40fd95b8e28af02cdfb96a63486f1a9321de52f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 13 Feb 2019 10:35:16 -0800 Subject: [PATCH] C++: Better tracking of SSA memory accesses This change fixes a few key problems with the existing SSA implementations: For unaliased SSA, we were incorrectly choosing to model a local variable that had accesses that did not cover the entire variable. This has been changed to ensure that all accesses to the variable are at offset zero and have the same type as the variable itself. This was only possible to fix now that every `MemoryOperand` has its own type. For aliased SSA, we now correctly track the offset and size of each memory access using an interval of bit offsets covered by the access. The offset interval makes the overlap computation more straightforward. Again, this is only possible now that operands have types. The `getXXXMemoryAccess` predicates are now driven by the `MemoryAccessKind` on the operands and results, instead of by specific opcodes. This change does fix an existing false negative in the IR dataflow tests. I added a few simple test cases to the SSA IR tests, covering the various kinds of overlap (MustExcactly, MustTotally, and MayPartially). I added "PrintSSA.qll", which can dump the SSA memory accesses as part of an IR dump. --- config/identical-files.json | 4 + .../ir/implementation/MemoryAccessKind.qll | 38 ++- .../aliased_ssa/Instruction.qll | 12 +- .../ir/implementation/aliased_ssa/Operand.qll | 6 +- .../aliased_ssa/internal/AliasedSSA.qll | 233 ++++++++++-------- .../aliased_ssa/internal/PrintAliasedSSA.qll | 12 - .../aliased_ssa/internal/PrintSSA.qll | 20 ++ .../cpp/ir/implementation/raw/Instruction.qll | 12 +- .../cpp/ir/implementation/raw/Operand.qll | 6 +- .../unaliased_ssa/Instruction.qll | 12 +- .../implementation/unaliased_ssa/Operand.qll | 6 +- .../unaliased_ssa/internal/PrintSSA.qll | 20 ++ .../unaliased_ssa/internal/SimpleSSA.qll | 43 +++- .../code/cpp/ir/internal/IntegerConstant.qll | 56 +++++ .../code/cpp/ir/internal/IntegerInterval.qll | 33 +++ .../dataflow-tests/test_diff.expected | 1 + .../dataflow/dataflow-tests/test_ir.expected | 1 + .../ir/ssa/aliased_ssa_ir.expected | 157 ++++++++++++ cpp/ql/test/library-tests/ir/ssa/ssa.cpp | 33 +++ .../ir/ssa/unaliased_ssa_ir.expected | 147 +++++++++++ 20 files changed, 722 insertions(+), 130 deletions(-) delete mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll diff --git a/config/identical-files.json b/config/identical-files.json index 046ac536b5b..05ba2fc42d5 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -68,6 +68,10 @@ "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" ], + "C++ SSA PrintSSA": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll" + ], "C++ IR ValueNumber": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll index 28299b8c33a..ef674e26f9c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -6,6 +6,7 @@ private newtype TMemoryAccessKind = TBufferMemoryAccess() or TBufferMayMemoryAccess() or TEscapedMemoryAccess() or + TEscapedMayMemoryAccess() or TPhiMemoryAccess() or TUnmodeledMemoryAccess() or TChiTotalMemoryAccess() or @@ -16,7 +17,17 @@ private newtype TMemoryAccessKind = * memory result. */ class MemoryAccessKind extends TMemoryAccessKind { - abstract string toString(); + string toString() { + none() + } + + /** + * Holds if the operand or result accesses memory pointed to by the `AddressOperand` on the + * same instruction. + */ + predicate usesAddressOperand() { + none() + } } /** @@ -27,6 +38,10 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess { override string toString() { result = "indirect" } + + override final predicate usesAddressOperand() { + any() + } } /** @@ -37,6 +52,10 @@ class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess override string toString() { result = "indirect(may)" } + + override final predicate usesAddressOperand() { + any() + } } /** @@ -48,6 +67,10 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess { override string toString() { result = "buffer" } + + override final predicate usesAddressOperand() { + any() + } } /** @@ -59,6 +82,10 @@ class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess { override string toString() { result = "buffer(may)" } + + override final predicate usesAddressOperand() { + any() + } } /** @@ -70,6 +97,15 @@ class EscapedMemoryAccess extends MemoryAccessKind, TEscapedMemoryAccess { } } +/** + * The operand or result may access all memory whose address has escaped. + */ +class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess { + override string toString() { + result = "escaped(may)" + } +} + /** * The operand is a Phi operand, which accesses the same memory as its * definition. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 8e442fe7d56..cb144033ab5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -498,6 +498,16 @@ class Instruction extends Construction::TInstruction { none() } + /** + * Returns the operand that holds the memory address to which the instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is `r1`. + */ + final AddressOperand getResultAddressOperand() { + getResultMemoryAccess().usesAddressOperand() and + result.getUseInstruction() = this + } + /** * Holds if the result of this instruction is precisely modeled in SSA. Always * holds for a register result. For a memory result, a modeled result is @@ -1340,7 +1350,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { } override final MemoryAccessKind getResultMemoryAccess() { - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index 1aa9d1a9d6c..2be001ace75 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -128,7 +128,7 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess() instanceof IndirectMemoryAccess and + getMemoryAccess().usesAddressOperand() and result.getUseInstruction() = getUseInstruction() } } @@ -351,10 +351,10 @@ class SideEffectOperand extends TypedOperand { override MemoryAccessKind getMemoryAccess() { useInstr instanceof CallSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof CallReadSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof IndirectReadSideEffectInstruction and result instanceof IndirectMemoryAccess diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index 36d24f08f8b..543b1ecf799 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -1,11 +1,10 @@ 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.implementation.unaliased_ssa.IR private import semmle.code.cpp.ir.internal.IntegerConstant as Ints +private import semmle.code.cpp.ir.internal.IntegerInterval as Interval +private import semmle.code.cpp.ir.internal.OperandTag private class IntValue = Ints::IntValue; @@ -37,6 +36,9 @@ class VirtualVariable extends TVirtualVariable { } } +/** + * A virtual variable representing a single non-escaped `IRVariable`. + */ class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable { IRVariable var; @@ -52,7 +54,6 @@ class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable { result = var } - // REVIEW: This should just be on MemoryAccess override final Type getType() { result = var.getType() } @@ -62,6 +63,10 @@ class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable { } } +/** + * A virtual variable representing all escaped memory accessible by the function, + * including escaped local variables. + */ class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable { FunctionIR f; @@ -86,24 +91,35 @@ class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable { } } +private predicate hasResultMemoryAccess(Instruction instr, IRVariable var, IntValue startBitOffset, + IntValue endBitOffset) { + resultPointsTo(instr.getResultAddressOperand().getDefinitionInstruction(), var, startBitOffset) and + if exists(instr.getResultSize()) then + endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8)) + else + endBitOffset = Ints::unknown() +} + +private predicate hasOperandMemoryAccess(MemoryOperand operand, IRVariable var, IntValue startBitOffset, + IntValue endBitOffset) { + resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, startBitOffset) and + if exists(operand.getSize()) then + endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8)) + else + endBitOffset = Ints::unknown() +} + private newtype TMemoryAccess = - TVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) { - exists(Instruction instr | - exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and - resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, offset) and - if exists(instr.getResultSize()) - then instr.getResultSize() = size - else size = Ints::unknown() - ) + TVariableMemoryAccess(IRVariable var, IntValue startBitOffset, IntValue endBitOffset) { + hasResultMemoryAccess(_, var, startBitOffset, endBitOffset) or + hasOperandMemoryAccess(_, var, startBitOffset, endBitOffset) } or TUnknownMemoryAccess(UnknownVirtualVariable uvv) or TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv) -private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) { - result.getVariable() = var and - result.getOffset() = offset and - result.getSize() = size +private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue startBitOffset, IntValue endBitOffset) { + result = TVariableMemoryAccess(var, startBitOffset, endBitOffset) } class MemoryAccess extends TMemoryAccess { @@ -120,17 +136,27 @@ class MemoryAccess extends TMemoryAccess { } } +/** + * An access to memory within a single known `IRVariable`. The variable may be either an unescaped variable + * (with its own `VirtualIRVariable`) or an escaped variable (assiged to `UnknownVirtualVariable`). + */ class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess { IRVariable var; - IntValue offset; - IntValue size; + IntValue startBitOffset; + IntValue endBitOffset; VariableMemoryAccess() { - this = TVariableMemoryAccess(var, offset, size) + this = TVariableMemoryAccess(var, startBitOffset, endBitOffset) } override final string toString() { - result = var.toString() + "[" + offset.toString() + ".." + (offset + size - 1).toString() + "]" + exists(string partialString | + result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + partialString and + if isPartialMemoryAccess() then + partialString = " (partial)" + else + partialString = "" + ) } final override VirtualVariable getVirtualVariable() { @@ -138,12 +164,12 @@ class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess { not exists(getVirtualVariable(var)) and result = getUnknownVirtualVariable(var.getEnclosingFunctionIR()) } - IntValue getOffset() { - result = offset + IntValue getStartBitOffset() { + result = startBitOffset } - IntValue getSize() { - result = size + IntValue getEndBitOffset() { + result = endBitOffset } final IRVariable getVariable() { @@ -152,12 +178,15 @@ class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess { final override predicate isPartialMemoryAccess() { not exists(getVirtualVariable(var)) or - getOffset() != 0 + getStartBitOffset() != 0 or - getSize() != var.getType().getSize() + not Ints::isEQ(getEndBitOffset(), Ints::add(getStartBitOffset(), Ints::mul(var.getType().getSize(), 8))) } } +/** + * An access to memory that is not known to be confined to a specific `IRVariable`. + */ class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess { UnknownVirtualVariable vvar; @@ -176,12 +205,11 @@ class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess { final override predicate isPartialMemoryAccess() { any() } - - Type getType() { - result instanceof UnknownType - } } +/** + * An access to all aliased memory. + */ class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess { UnknownVirtualVariable vvar; @@ -196,84 +224,91 @@ class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess { 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 + def.getVirtualVariable() = use.getVirtualVariable() and + ( + // A TotalUnknownMemoryAccess must totally overlap any access to the same virtual variable. + def instanceof TotalUnknownMemoryAccess and result instanceof MustTotallyOverlap or + // An UnknownMemoryAccess may partially overlap any access to the same virtual variable. + def instanceof UnknownMemoryAccess and result instanceof MayPartiallyOverlap or + exists(VariableMemoryAccess defVariableAccess | + defVariableAccess = def and + ( + ( + // A VariableMemoryAccess may partially overlap an unknown access to the same virtual variable. + ((use instanceof UnknownMemoryAccess) or (use instanceof TotalUnknownMemoryAccess)) and + result instanceof MayPartiallyOverlap + ) or + // A VariableMemoryAccess overlaps another access to the same variable based on the relationship + // of the two offset intervals. + exists(VariableMemoryAccess useVariableAccess, IntValue defStartOffset, IntValue defEndOffset, + IntValue useStartOffset, IntValue useEndOffset | + useVariableAccess = use and + defStartOffset = defVariableAccess.getStartBitOffset() and + defEndOffset = defVariableAccess.getEndBitOffset() and + useStartOffset = useVariableAccess.getStartBitOffset() and + useEndOffset = useVariableAccess.getEndBitOffset() and + result = Interval::getOverlap(defStartOffset, defEndOffset, useStartOffset, useEndOffset) + ) + ) + ) ) } 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.getEnclosingFunctionIR())) and - not instr instanceof UnmodeledDefinitionInstruction and - not instr instanceof AliasedDefinitionInstruction - or - result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingFunctionIR())) and - instr instanceof AliasedDefinitionInstruction + exists(MemoryAccessKind kind | + kind = instr.getResultMemoryAccess() and + ( + ( + kind.usesAddressOperand() and + if hasResultMemoryAccess(instr, _, _, _) then ( + exists(IRVariable var, IntValue startBitOffset, IntValue endBitOffset | + hasResultMemoryAccess(instr, var, startBitOffset, endBitOffset) and + result = getVariableMemoryAccess(var, startBitOffset, endBitOffset) + ) + ) + else ( + result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingFunctionIR())) + ) + ) or + ( + kind instanceof EscapedMemoryAccess and + result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingFunctionIR())) + ) or + ( + kind instanceof EscapedMayMemoryAccess and + result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingFunctionIR())) + ) + ) ) } MemoryAccess getOperandMemoryAccess(MemoryOperand operand) { - if exists(IRVariable var, IntValue i | - resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) - ) - then exists(IRVariable var, IntValue i, int size | - resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and - result = getVariableMemoryAccess(var, i, size) and - size = operand.getDefinitionInstruction().getResultSize() - ) - else ( - result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getUseInstruction().getEnclosingFunctionIR())) and - not operand.getUseInstruction() instanceof UnmodeledUseInstruction + exists(MemoryAccessKind kind | + kind = operand.getMemoryAccess() and + ( + ( + kind.usesAddressOperand() and + if hasOperandMemoryAccess(operand, _, _, _) then ( + exists(IRVariable var, IntValue startBitOffset, IntValue endBitOffset | + hasOperandMemoryAccess(operand, var, startBitOffset, endBitOffset) and + result = getVariableMemoryAccess(var, startBitOffset, endBitOffset) + ) + ) + else ( + result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingFunctionIR())) + ) + ) or + ( + kind instanceof EscapedMemoryAccess and + result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingFunctionIR())) + ) or + ( + kind instanceof EscapedMayMemoryAccess and + result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingFunctionIR())) + ) + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll deleted file mode 100644 index d3889ad8d0e..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll +++ /dev/null @@ -1,12 +0,0 @@ -private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR -private import AliasedSSA - -/** - * Property provide that dumps the memory access of each result. Useful for debugging SSA - * construction. - */ -class PropertyProvider extends IRPropertyProvider { - override string getInstructionProperty(Instruction instruction, string key) { - key = "ResultMemoryAccess" and result = getResultMemoryAccess(instruction).toString() - } -} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll new file mode 100644 index 00000000000..4461ffe184d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll @@ -0,0 +1,20 @@ +private import SSAConstructionInternal +private import OldIR +private import Alias + +/** + * Property provide that dumps the memory access of each result. Useful for debugging SSA + * construction. + */ +class PropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + ( + key = "ResultMemoryAccess" and + result = getResultMemoryAccess(instruction).toString() + ) or + ( + key = "OperandMemoryAccess" and + result = getOperandMemoryAccess(instruction.getAnOperand().(MemoryOperand)).toString() + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 8e442fe7d56..cb144033ab5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -498,6 +498,16 @@ class Instruction extends Construction::TInstruction { none() } + /** + * Returns the operand that holds the memory address to which the instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is `r1`. + */ + final AddressOperand getResultAddressOperand() { + getResultMemoryAccess().usesAddressOperand() and + result.getUseInstruction() = this + } + /** * Holds if the result of this instruction is precisely modeled in SSA. Always * holds for a register result. For a memory result, a modeled result is @@ -1340,7 +1350,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { } override final MemoryAccessKind getResultMemoryAccess() { - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index 1aa9d1a9d6c..2be001ace75 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -128,7 +128,7 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess() instanceof IndirectMemoryAccess and + getMemoryAccess().usesAddressOperand() and result.getUseInstruction() = getUseInstruction() } } @@ -351,10 +351,10 @@ class SideEffectOperand extends TypedOperand { override MemoryAccessKind getMemoryAccess() { useInstr instanceof CallSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof CallReadSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof IndirectReadSideEffectInstruction and result instanceof IndirectMemoryAccess diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 8e442fe7d56..cb144033ab5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -498,6 +498,16 @@ class Instruction extends Construction::TInstruction { none() } + /** + * Returns the operand that holds the memory address to which the instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is `r1`. + */ + final AddressOperand getResultAddressOperand() { + getResultMemoryAccess().usesAddressOperand() and + result.getUseInstruction() = this + } + /** * Holds if the result of this instruction is precisely modeled in SSA. Always * holds for a register result. For a memory result, a modeled result is @@ -1340,7 +1350,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { } override final MemoryAccessKind getResultMemoryAccess() { - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index 1aa9d1a9d6c..2be001ace75 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -128,7 +128,7 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess() instanceof IndirectMemoryAccess and + getMemoryAccess().usesAddressOperand() and result.getUseInstruction() = getUseInstruction() } } @@ -351,10 +351,10 @@ class SideEffectOperand extends TypedOperand { override MemoryAccessKind getMemoryAccess() { useInstr instanceof CallSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof CallReadSideEffectInstruction and - result instanceof EscapedMemoryAccess + result instanceof EscapedMayMemoryAccess or useInstr instanceof IndirectReadSideEffectInstruction and result instanceof IndirectMemoryAccess diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll new file mode 100644 index 00000000000..4461ffe184d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll @@ -0,0 +1,20 @@ +private import SSAConstructionInternal +private import OldIR +private import Alias + +/** + * Property provide that dumps the memory access of each result. Useful for debugging SSA + * construction. + */ +class PropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + ( + key = "ResultMemoryAccess" and + result = getResultMemoryAccess(instruction).toString() + ) or + ( + key = "OperandMemoryAccess" and + result = getOperandMemoryAccess(instruction.getAnOperand().(MemoryOperand)).toString() + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 509294a7908..aaf59e4ac60 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -1,12 +1,46 @@ import AliasAnalysis import cpp private import semmle.code.cpp.ir.implementation.raw.IR +private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private import semmle.code.cpp.ir.internal.OperandTag private import semmle.code.cpp.ir.internal.Overlap +private class IntValue = Ints::IntValue; + +private predicate hasResultMemoryAccess(Instruction instr, IRVariable var, Type type, IntValue bitOffset) { + resultPointsTo(instr.getResultAddressOperand().getDefinitionInstruction(), var, bitOffset) and + type = instr.getResultType() +} + +private predicate hasOperandMemoryAccess(MemoryOperand operand, IRVariable var, Type type, IntValue bitOffset) { + resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, bitOffset) and + type = operand.getType() +} + +/** + * Holds if the specified variable should be modeled in SSA form. For unaliased SSA, we only model a variable if its + * address never escapes and all reads and writes of that variable access the entire variable using the original type + * of the variable. + */ +private predicate isVariableModeled(IRVariable var) { + not variableAddressEscapes(var) and + // There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for + // `type = var.getType()` is sufficient. + forall(Instruction instr, Type type, IntValue bitOffset | + hasResultMemoryAccess(instr, var, type, bitOffset) | + bitOffset = 0 and + type = var.getType() + ) and + forall(MemoryOperand operand, Type type, IntValue bitOffset | + hasOperandMemoryAccess(operand, var, type, bitOffset) | + bitOffset = 0 and + type = var.getType() + ) +} + private newtype TVirtualVariable = MkVirtualVariable(IRVariable var) { - not variableAddressEscapes(var) + isVariableModeled(var) } private VirtualVariable getVirtualVariable(IRVariable var) { @@ -28,7 +62,6 @@ class VirtualVariable extends TVirtualVariable { result = var } - // REVIEW: This should just be on MemoryAccess final Type getType() { result = var.getType() } @@ -74,16 +107,14 @@ Overlap getOverlap(MemoryAccess def, MemoryAccess use) { MemoryAccess getResultMemoryAccess(Instruction instr) { exists(IRVariable var | - instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and - resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), - var, 0) and + hasResultMemoryAccess(instr, var, _, _) and result = getMemoryAccess(var) ) } MemoryAccess getOperandMemoryAccess(MemoryOperand operand) { exists(IRVariable var | - resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and + hasOperandMemoryAccess(operand, var, _, _) and result = getMemoryAccess(var) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll index 1c29e4618a0..ee454bda688 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll @@ -31,6 +31,14 @@ predicate hasValue(IntValue n) { n != unknown() } +/** + * Returns a string representation of `n`. If `n` does not have a known value, the result is "??". + */ +bindingset[n] +string intValueToString(IntValue n) { + if hasValue(n) then result = n.toString() else result = "??" +} + /** * Holds if the value `f` is within the range of representable integers. */ @@ -199,3 +207,51 @@ bindingset[a] IntValue neg(IntValue a) { result = -a // -INT_MIN = INT_MIN, so this preserves unknown } + +/** + * Holds if `a` is equal to `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isEQ(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a = b +} + +/** + * Holds if `a` is not equal to `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isNE(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a != b +} + +/** + * Holds if `a` is less than `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isLT(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a < b +} + +/** + * Holds if `a` is less than or equal to `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isLE(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a <= b +} + +/** + * Holds if `a` is greater than `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isGT(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a > b +} + +/** + * Holds if `a` is greater than or equal to `b`. Does not hold if either `a` or `b` is unknown. + */ +bindingset[a, b] +predicate isGE(IntValue a, IntValue b) { + hasValue(a) and hasValue(b) and a >= b +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll new file mode 100644 index 00000000000..986361ada58 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll @@ -0,0 +1,33 @@ +/** + * Support for integer intervals. + * An interval is represented as by its inclusive lower bound, `start`, and its exclusive upper bound, `end`. + * Either or both of `start` and `end` may have an unknown value. + */ + +import Overlap +private import IntegerConstant + +/** + * Gets the overlap relationship between the definition interval [`defStart`, `defEnd`) and the use interval + * [`useStart`, `useEnd`). + */ +bindingset[defStart, defEnd, useStart, useEnd] +Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntValue useEnd) { + if isEQ(defStart, useStart) and isEQ(defEnd, useEnd) then + result instanceof MustExactlyOverlap + else if isLE(defStart, useStart) and isGE(defEnd, useEnd) then + result instanceof MustTotallyOverlap + else if isLE(defEnd, useStart) or isGE(defStart, useEnd) then + none() + else + result instanceof MayPartiallyOverlap +} + +/** + * Gets a string representation of the interval [`start`, `end`). + */ +bindingset[start, end] +string getIntervalString(IntValue start, IntValue end) { + // We represent an interval has half-open, so print it as "[start..end)". + result = "[" + intValueToString(start) + ".." + intValueToString(end) + ")" +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected index 6cd0d7a9404..b7f2ae6d6ef 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected @@ -1,6 +1,7 @@ | test.cpp:66:30:66:36 | test.cpp:71:8:71:9 | AST only | | test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only | | test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only | +| test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | IR only | | test.cpp:120:9:120:20 | test.cpp:126:8:126:19 | AST only | | test.cpp:122:18:122:30 | test.cpp:132:22:132:23 | IR only | | test.cpp:122:18:122:30 | test.cpp:140:22:140:23 | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected index db429c84c73..5e3d4970bd7 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected @@ -11,6 +11,7 @@ | test.cpp:86:8:86:9 | Load: i1 | test.cpp:83:7:83:8 | Uninitialized: definition of u2 | | test.cpp:90:8:90:14 | Load: source1 | test.cpp:89:28:89:34 | InitializeParameter: source1 | | test.cpp:92:8:92:14 | Load: source1 | test.cpp:89:28:89:34 | InitializeParameter: source1 | +| test.cpp:110:10:110:12 | Load: (reference dereference) | test.cpp:109:9:109:14 | Call: call to source | | test.cpp:132:22:132:23 | Load: m1 | test.cpp:122:18:122:30 | InitializeParameter: sourceStruct1 | | test.cpp:140:22:140:23 | Load: m1 | test.cpp:122:18:122:30 | InitializeParameter: sourceStruct1 | | test.cpp:188:8:188:8 | Load: y | test.cpp:186:27:186:32 | Call: call to source | diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 5ee5b5d11dc..1df580e0238 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -235,3 +235,160 @@ ssa.cpp: # 69| v3_10(void) = ConditionalBranch : r3_9 #-----| False -> Block 2 #-----| True -> Block 1 + +# 75| void MustExactlyOverlap(Point) +# 75| Block 0 +# 75| v0_0(void) = EnterFunction : +# 75| m0_1(unknown) = AliasedDefinition : +# 75| mu0_2(unknown) = UnmodeledDefinition : +# 75| r0_3(glval) = VariableAddress[a] : +# 75| m0_4(Point) = InitializeParameter[a] : r0_3 +# 76| r0_5(glval) = VariableAddress[b] : +# 76| r0_6(glval) = VariableAddress[a] : +# 76| r0_7(Point) = Load : r0_6, m0_4 +# 76| m0_8(Point) = Store : r0_5, r0_7 +# 77| v0_9(void) = NoOp : +# 75| v0_10(void) = ReturnVoid : +# 75| v0_11(void) = UnmodeledUse : mu* +# 75| v0_12(void) = ExitFunction : + +# 79| void MustExactlyOverlapEscaped(Point) +# 79| Block 0 +# 79| v0_0(void) = EnterFunction : +# 79| m0_1(unknown) = AliasedDefinition : +# 79| mu0_2(unknown) = UnmodeledDefinition : +# 79| r0_3(glval) = VariableAddress[a] : +# 79| m0_4(Point) = InitializeParameter[a] : r0_3 +# 79| m0_5(unknown) = Chi : m0_1, m0_4 +# 80| r0_6(glval) = VariableAddress[b] : +# 80| r0_7(glval) = VariableAddress[a] : +# 80| r0_8(Point) = Load : r0_7, m0_5 +# 80| m0_9(Point) = Store : r0_6, r0_8 +# 81| r0_10(glval) = FunctionAddress[Escape] : +# 81| r0_11(glval) = VariableAddress[a] : +# 81| r0_12(void *) = Convert : r0_11 +# 81| v0_13(void) = Call : r0_10, r0_12 +# 81| m0_14(unknown) = ^CallSideEffect : m0_5 +# 81| m0_15(unknown) = Chi : m0_5, m0_14 +# 82| v0_16(void) = NoOp : +# 79| v0_17(void) = ReturnVoid : +# 79| v0_18(void) = UnmodeledUse : mu* +# 79| v0_19(void) = ExitFunction : + +# 84| void MustTotallyOverlap(Point) +# 84| Block 0 +# 84| v0_0(void) = EnterFunction : +# 84| m0_1(unknown) = AliasedDefinition : +# 84| mu0_2(unknown) = UnmodeledDefinition : +# 84| r0_3(glval) = VariableAddress[a] : +# 84| m0_4(Point) = InitializeParameter[a] : r0_3 +# 85| r0_5(glval) = VariableAddress[x] : +# 85| r0_6(glval) = VariableAddress[a] : +# 85| r0_7(glval) = FieldAddress[x] : r0_6 +# 85| r0_8(int) = Load : r0_7, m0_4 +# 85| m0_9(int) = Store : r0_5, r0_8 +# 86| r0_10(glval) = VariableAddress[y] : +# 86| r0_11(glval) = VariableAddress[a] : +# 86| r0_12(glval) = FieldAddress[y] : r0_11 +# 86| r0_13(int) = Load : r0_12, m0_4 +# 86| m0_14(int) = Store : r0_10, r0_13 +# 87| v0_15(void) = NoOp : +# 84| v0_16(void) = ReturnVoid : +# 84| v0_17(void) = UnmodeledUse : mu* +# 84| v0_18(void) = ExitFunction : + +# 89| void MustTotallyOverlapEscaped(Point) +# 89| Block 0 +# 89| v0_0(void) = EnterFunction : +# 89| m0_1(unknown) = AliasedDefinition : +# 89| mu0_2(unknown) = UnmodeledDefinition : +# 89| r0_3(glval) = VariableAddress[a] : +# 89| m0_4(Point) = InitializeParameter[a] : r0_3 +# 89| m0_5(unknown) = Chi : m0_1, m0_4 +# 90| r0_6(glval) = VariableAddress[x] : +# 90| r0_7(glval) = VariableAddress[a] : +# 90| r0_8(glval) = FieldAddress[x] : r0_7 +# 90| r0_9(int) = Load : r0_8, m0_5 +# 90| m0_10(int) = Store : r0_6, r0_9 +# 91| r0_11(glval) = VariableAddress[y] : +# 91| r0_12(glval) = VariableAddress[a] : +# 91| r0_13(glval) = FieldAddress[y] : r0_12 +# 91| r0_14(int) = Load : r0_13, m0_5 +# 91| m0_15(int) = Store : r0_11, r0_14 +# 92| r0_16(glval) = FunctionAddress[Escape] : +# 92| r0_17(glval) = VariableAddress[a] : +# 92| r0_18(void *) = Convert : r0_17 +# 92| v0_19(void) = Call : r0_16, r0_18 +# 92| m0_20(unknown) = ^CallSideEffect : m0_5 +# 92| m0_21(unknown) = Chi : m0_5, m0_20 +# 93| v0_22(void) = NoOp : +# 89| v0_23(void) = ReturnVoid : +# 89| v0_24(void) = UnmodeledUse : mu* +# 89| v0_25(void) = ExitFunction : + +# 95| void MayPartiallyOverlap(int, int) +# 95| Block 0 +# 95| v0_0(void) = EnterFunction : +# 95| m0_1(unknown) = AliasedDefinition : +# 95| mu0_2(unknown) = UnmodeledDefinition : +# 95| r0_3(glval) = VariableAddress[x] : +# 95| m0_4(int) = InitializeParameter[x] : r0_3 +# 95| r0_5(glval) = VariableAddress[y] : +# 95| m0_6(int) = InitializeParameter[y] : r0_5 +# 96| r0_7(glval) = VariableAddress[a] : +# 96| m0_8(Point) = Uninitialized[a] : r0_7 +# 96| r0_9(glval) = FieldAddress[x] : r0_7 +# 96| r0_10(glval) = VariableAddress[x] : +# 96| r0_11(int) = Load : r0_10, m0_4 +# 96| m0_12(int) = Store : r0_9, r0_11 +# 96| m0_13(Point) = Chi : m0_8, m0_12 +# 96| r0_14(glval) = FieldAddress[y] : r0_7 +# 96| r0_15(glval) = VariableAddress[y] : +# 96| r0_16(int) = Load : r0_15, m0_6 +# 96| m0_17(int) = Store : r0_14, r0_16 +# 96| m0_18(Point) = Chi : m0_13, m0_17 +# 97| r0_19(glval) = VariableAddress[b] : +# 97| r0_20(glval) = VariableAddress[a] : +# 97| r0_21(Point) = Load : r0_20, m0_18 +# 97| m0_22(Point) = Store : r0_19, r0_21 +# 98| v0_23(void) = NoOp : +# 95| v0_24(void) = ReturnVoid : +# 95| v0_25(void) = UnmodeledUse : mu* +# 95| v0_26(void) = ExitFunction : + +# 100| void MayPartiallyOverlapEscaped(int, int) +# 100| Block 0 +# 100| v0_0(void) = EnterFunction : +# 100| m0_1(unknown) = AliasedDefinition : +# 100| mu0_2(unknown) = UnmodeledDefinition : +# 100| r0_3(glval) = VariableAddress[x] : +# 100| m0_4(int) = InitializeParameter[x] : r0_3 +# 100| r0_5(glval) = VariableAddress[y] : +# 100| m0_6(int) = InitializeParameter[y] : r0_5 +# 101| r0_7(glval) = VariableAddress[a] : +# 101| m0_8(Point) = Uninitialized[a] : r0_7 +# 101| m0_9(unknown) = Chi : m0_1, m0_8 +# 101| r0_10(glval) = FieldAddress[x] : r0_7 +# 101| r0_11(glval) = VariableAddress[x] : +# 101| r0_12(int) = Load : r0_11, m0_4 +# 101| m0_13(int) = Store : r0_10, r0_12 +# 101| m0_14(unknown) = Chi : m0_9, m0_13 +# 101| r0_15(glval) = FieldAddress[y] : r0_7 +# 101| r0_16(glval) = VariableAddress[y] : +# 101| r0_17(int) = Load : r0_16, m0_6 +# 101| m0_18(int) = Store : r0_15, r0_17 +# 101| m0_19(unknown) = Chi : m0_14, m0_18 +# 102| r0_20(glval) = VariableAddress[b] : +# 102| r0_21(glval) = VariableAddress[a] : +# 102| r0_22(Point) = Load : r0_21, m0_19 +# 102| m0_23(Point) = Store : r0_20, r0_22 +# 103| r0_24(glval) = FunctionAddress[Escape] : +# 103| r0_25(glval) = VariableAddress[a] : +# 103| r0_26(void *) = Convert : r0_25 +# 103| v0_27(void) = Call : r0_24, r0_26 +# 103| m0_28(unknown) = ^CallSideEffect : m0_19 +# 103| m0_29(unknown) = Chi : m0_19, m0_28 +# 104| v0_30(void) = NoOp : +# 100| v0_31(void) = ReturnVoid : +# 100| v0_32(void) = UnmodeledUse : mu* +# 100| v0_33(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index f70387cdcba..7d5357c0f78 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -69,3 +69,36 @@ void chiNodeAtEndOfLoop(int n, char* p) { while (n-- > 0) * p++ = 0; } + +void Escape(void* p); + +void MustExactlyOverlap(Point a) { + Point b = a; +} + +void MustExactlyOverlapEscaped(Point a) { + Point b = a; + Escape(&a); +} + +void MustTotallyOverlap(Point a) { + int x = a.x; + int y = a.y; +} + +void MustTotallyOverlapEscaped(Point a) { + int x = a.x; + int y = a.y; + Escape(&a); +} + +void MayPartiallyOverlap(int x, int y) { + Point a = { x, y }; + Point b = a; +} + +void MayPartiallyOverlapEscaped(int x, int y) { + Point a = { x, y }; + Point b = a; + Escape(&a); +} diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index a3b4e00a0d1..67ed4a41812 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -236,3 +236,150 @@ ssa.cpp: # 69| v3_9(void) = ConditionalBranch : r3_8 #-----| False -> Block 2 #-----| True -> Block 1 + +# 75| void MustExactlyOverlap(Point) +# 75| Block 0 +# 75| v0_0(void) = EnterFunction : +# 75| mu0_1(unknown) = AliasedDefinition : +# 75| mu0_2(unknown) = UnmodeledDefinition : +# 75| r0_3(glval) = VariableAddress[a] : +# 75| m0_4(Point) = InitializeParameter[a] : r0_3 +# 76| r0_5(glval) = VariableAddress[b] : +# 76| r0_6(glval) = VariableAddress[a] : +# 76| r0_7(Point) = Load : r0_6, m0_4 +# 76| m0_8(Point) = Store : r0_5, r0_7 +# 77| v0_9(void) = NoOp : +# 75| v0_10(void) = ReturnVoid : +# 75| v0_11(void) = UnmodeledUse : mu* +# 75| v0_12(void) = ExitFunction : + +# 79| void MustExactlyOverlapEscaped(Point) +# 79| Block 0 +# 79| v0_0(void) = EnterFunction : +# 79| mu0_1(unknown) = AliasedDefinition : +# 79| mu0_2(unknown) = UnmodeledDefinition : +# 79| r0_3(glval) = VariableAddress[a] : +# 79| mu0_4(Point) = InitializeParameter[a] : r0_3 +# 80| r0_5(glval) = VariableAddress[b] : +# 80| r0_6(glval) = VariableAddress[a] : +# 80| r0_7(Point) = Load : r0_6, mu0_2 +# 80| m0_8(Point) = Store : r0_5, r0_7 +# 81| r0_9(glval) = FunctionAddress[Escape] : +# 81| r0_10(glval) = VariableAddress[a] : +# 81| r0_11(void *) = Convert : r0_10 +# 81| v0_12(void) = Call : r0_9, r0_11 +# 81| mu0_13(unknown) = ^CallSideEffect : mu0_2 +# 82| v0_14(void) = NoOp : +# 79| v0_15(void) = ReturnVoid : +# 79| v0_16(void) = UnmodeledUse : mu* +# 79| v0_17(void) = ExitFunction : + +# 84| void MustTotallyOverlap(Point) +# 84| Block 0 +# 84| v0_0(void) = EnterFunction : +# 84| mu0_1(unknown) = AliasedDefinition : +# 84| mu0_2(unknown) = UnmodeledDefinition : +# 84| r0_3(glval) = VariableAddress[a] : +# 84| mu0_4(Point) = InitializeParameter[a] : r0_3 +# 85| r0_5(glval) = VariableAddress[x] : +# 85| r0_6(glval) = VariableAddress[a] : +# 85| r0_7(glval) = FieldAddress[x] : r0_6 +# 85| r0_8(int) = Load : r0_7, mu0_2 +# 85| m0_9(int) = Store : r0_5, r0_8 +# 86| r0_10(glval) = VariableAddress[y] : +# 86| r0_11(glval) = VariableAddress[a] : +# 86| r0_12(glval) = FieldAddress[y] : r0_11 +# 86| r0_13(int) = Load : r0_12, mu0_2 +# 86| m0_14(int) = Store : r0_10, r0_13 +# 87| v0_15(void) = NoOp : +# 84| v0_16(void) = ReturnVoid : +# 84| v0_17(void) = UnmodeledUse : mu* +# 84| v0_18(void) = ExitFunction : + +# 89| void MustTotallyOverlapEscaped(Point) +# 89| Block 0 +# 89| v0_0(void) = EnterFunction : +# 89| mu0_1(unknown) = AliasedDefinition : +# 89| mu0_2(unknown) = UnmodeledDefinition : +# 89| r0_3(glval) = VariableAddress[a] : +# 89| mu0_4(Point) = InitializeParameter[a] : r0_3 +# 90| r0_5(glval) = VariableAddress[x] : +# 90| r0_6(glval) = VariableAddress[a] : +# 90| r0_7(glval) = FieldAddress[x] : r0_6 +# 90| r0_8(int) = Load : r0_7, mu0_2 +# 90| m0_9(int) = Store : r0_5, r0_8 +# 91| r0_10(glval) = VariableAddress[y] : +# 91| r0_11(glval) = VariableAddress[a] : +# 91| r0_12(glval) = FieldAddress[y] : r0_11 +# 91| r0_13(int) = Load : r0_12, mu0_2 +# 91| m0_14(int) = Store : r0_10, r0_13 +# 92| r0_15(glval) = FunctionAddress[Escape] : +# 92| r0_16(glval) = VariableAddress[a] : +# 92| r0_17(void *) = Convert : r0_16 +# 92| v0_18(void) = Call : r0_15, r0_17 +# 92| mu0_19(unknown) = ^CallSideEffect : mu0_2 +# 93| v0_20(void) = NoOp : +# 89| v0_21(void) = ReturnVoid : +# 89| v0_22(void) = UnmodeledUse : mu* +# 89| v0_23(void) = ExitFunction : + +# 95| void MayPartiallyOverlap(int, int) +# 95| Block 0 +# 95| v0_0(void) = EnterFunction : +# 95| mu0_1(unknown) = AliasedDefinition : +# 95| mu0_2(unknown) = UnmodeledDefinition : +# 95| r0_3(glval) = VariableAddress[x] : +# 95| m0_4(int) = InitializeParameter[x] : r0_3 +# 95| r0_5(glval) = VariableAddress[y] : +# 95| m0_6(int) = InitializeParameter[y] : r0_5 +# 96| r0_7(glval) = VariableAddress[a] : +# 96| mu0_8(Point) = Uninitialized[a] : r0_7 +# 96| r0_9(glval) = FieldAddress[x] : r0_7 +# 96| r0_10(glval) = VariableAddress[x] : +# 96| r0_11(int) = Load : r0_10, m0_4 +# 96| mu0_12(int) = Store : r0_9, r0_11 +# 96| r0_13(glval) = FieldAddress[y] : r0_7 +# 96| r0_14(glval) = VariableAddress[y] : +# 96| r0_15(int) = Load : r0_14, m0_6 +# 96| mu0_16(int) = Store : r0_13, r0_15 +# 97| r0_17(glval) = VariableAddress[b] : +# 97| r0_18(glval) = VariableAddress[a] : +# 97| r0_19(Point) = Load : r0_18, mu0_2 +# 97| m0_20(Point) = Store : r0_17, r0_19 +# 98| v0_21(void) = NoOp : +# 95| v0_22(void) = ReturnVoid : +# 95| v0_23(void) = UnmodeledUse : mu* +# 95| v0_24(void) = ExitFunction : + +# 100| void MayPartiallyOverlapEscaped(int, int) +# 100| Block 0 +# 100| v0_0(void) = EnterFunction : +# 100| mu0_1(unknown) = AliasedDefinition : +# 100| mu0_2(unknown) = UnmodeledDefinition : +# 100| r0_3(glval) = VariableAddress[x] : +# 100| m0_4(int) = InitializeParameter[x] : r0_3 +# 100| r0_5(glval) = VariableAddress[y] : +# 100| m0_6(int) = InitializeParameter[y] : r0_5 +# 101| r0_7(glval) = VariableAddress[a] : +# 101| mu0_8(Point) = Uninitialized[a] : r0_7 +# 101| r0_9(glval) = FieldAddress[x] : r0_7 +# 101| r0_10(glval) = VariableAddress[x] : +# 101| r0_11(int) = Load : r0_10, m0_4 +# 101| mu0_12(int) = Store : r0_9, r0_11 +# 101| r0_13(glval) = FieldAddress[y] : r0_7 +# 101| r0_14(glval) = VariableAddress[y] : +# 101| r0_15(int) = Load : r0_14, m0_6 +# 101| mu0_16(int) = Store : r0_13, r0_15 +# 102| r0_17(glval) = VariableAddress[b] : +# 102| r0_18(glval) = VariableAddress[a] : +# 102| r0_19(Point) = Load : r0_18, mu0_2 +# 102| m0_20(Point) = Store : r0_17, r0_19 +# 103| r0_21(glval) = FunctionAddress[Escape] : +# 103| r0_22(glval) = VariableAddress[a] : +# 103| r0_23(void *) = Convert : r0_22 +# 103| v0_24(void) = Call : r0_21, r0_23 +# 103| mu0_25(unknown) = ^CallSideEffect : mu0_2 +# 104| v0_26(void) = NoOp : +# 100| v0_27(void) = ReturnVoid : +# 100| v0_28(void) = UnmodeledUse : mu* +# 100| v0_29(void) = ExitFunction :