diff --git a/config/identical-files.json b/config/identical-files.json index e57bf00ca23..c93a2b28ae6 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -190,9 +190,14 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll" ], - "C++ SSA AliasAnalysis": [ + "SSA AliasAnalysis": [ "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" + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll" + ], + "C++ SSA AliasAnalysisImports": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll" ], "C++ IR ValueNumberingImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll", @@ -203,6 +208,10 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll" ], + "IR AliasConfiguration (unaliased_ssa)": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll" + ], "IR 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", 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 f5c6de314ab..eac4d333afc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -1,6 +1,7 @@ private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or + TEntireAllocationMemoryAccess() or TEscapedMemoryAccess() or TNonLocalMemoryAccess() or TPhiMemoryAccess() or @@ -43,6 +44,16 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess { final override predicate usesAddressOperand() { any() } } +/** + * The operand or results accesses all memory in the contiguous allocation that contains the address + * specified by the `AddressOperand` on the same instruction. + */ +class EntireAllocationMemoryAccess extends MemoryAccessKind, TEntireAllocationMemoryAccess { + override string toString() { result = "alloc" } + + final override predicate usesAddressOperand() { any() } +} + /** * The operand or result accesses all memory whose address has escaped. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index d39d8c30a1a..4b1124cf27e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -232,6 +232,31 @@ abstract class BufferReadOpcode extends BufferAccessOpcode { final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess } } +/** + * An opcode that access an entire memory allocation. + */ +abstract class EntireAllocationAccessOpcode extends Opcode { + final override predicate hasAddressOperand() { any() } +} + +/** + * An opcode that write to an entire memory allocation. + */ +abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + +/** + * An opcode that reads from an entire memory allocation. + */ +abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + /** * An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`. */ @@ -325,7 +350,7 @@ module Opcode { final override string toString() { result = "InitializeParameter" } } - class InitializeIndirection extends IndirectWriteOpcode, TInitializeIndirection { + class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } @@ -349,7 +374,7 @@ module Opcode { final override string toString() { result = "ReturnVoid" } } - class ReturnIndirection extends IndirectReadOpcode, TReturnIndirection { + class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } final override predicate hasOperandInternal(OperandTag tag) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index ff4602347ae..e2d3828fc52 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -1,21 +1,9 @@ private import AliasAnalysisInternal -private import cpp private import InputIR -private import semmle.code.cpp.ir.internal.IntegerConstant as Ints -private import semmle.code.cpp.ir.implementation.IRConfiguration -private import semmle.code.cpp.models.interfaces.Alias +private import AliasAnalysisImports private class IntValue = Ints::IntValue; -/** - * Gets the offset of field `field` in bits. - */ -private IntValue getFieldBitOffset(Field field) { - if field instanceof BitField - then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset()) - else result = Ints::mul(field.getByteOffset(), 8) -} - /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -36,7 +24,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { instr instanceof PointerDiffInstruction or // Converting an address to a `bool` does not escape the address. - instr.(ConvertInstruction).getResultType() instanceof BoolType + instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType ) ) or @@ -111,13 +99,10 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() or // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, Type resultType | + exists(ConvertInstruction convert, IRType resultType | convert = instr and - resultType = convert.getResultType() and - ( - resultType instanceof PointerType or - resultType instanceof Class //REVIEW: Remove when all glvalues are pointers - ) and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and bitOffset = 0 ) or @@ -131,7 +116,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { or // Computing a field address from a pointer propagates the address plus the // offset of the field. - bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or // A copy propagates the source value. operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 @@ -212,7 +197,7 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { } private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { - exists(Function f | + exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( @@ -223,27 +208,27 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In init.getEnclosingFunction() = f and operand instanceof ThisArgumentOperand ) and - not f.isVirtual() and - not f instanceof AliasFunction + not Language::isFunctionVirtual(f) and + not f instanceof AliasModels::AliasFunction ) } private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) ) } private predicate isOnlyEscapesViaReturnArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) ) } private predicate isNeverEscapesArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) ) @@ -265,61 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) { } /** - * Holds if the address of the specified local variable or parameter escapes the - * domain of the analysis. + * Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur + * either because the allocation's address is taken within the function and escapes, or because the + * allocation is marked as always escaping via `alwaysEscapes()`. */ -private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { - // The variable's address escapes if the result of any - // VariableAddressInstruction that computes the variable's address escapes. - exists(VariableAddressInstruction instr | - instr.getIRVariable() = var and - resultEscapesNonReturn(instr) - ) -} - -/** - * Holds if the address of the specified variable escapes the domain of the - * analysis. - */ -predicate variableAddressEscapes(IRVariable var) { +predicate allocationEscapes(Configuration::Allocation allocation) { + allocation.alwaysEscapes() + or exists(IREscapeAnalysisConfiguration config | - config.useSoundEscapeAnalysis() and - automaticVariableAddressEscapes(var.(IRAutomaticVariable)) + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) - or - // All variables with static storage duration have their address escape, even when escape analysis - // is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global - // variable. Normally, we rely on `AliasedDefinition` to handle that. - not var instanceof IRAutomaticVariable } /** - * Holds if the result of instruction `instr` points within variable `var`, at - * bit offset `bitOffset` within the variable. If the result points within - * `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown. + * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) { - // The address of a variable points to that variable, at offset 0. - instr.(VariableAddressInstruction).getIRVariable() = var and - bitOffset = 0 +private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { + operandIsPropagated(operand, bitOffset) or - // A string literal is just a special read-only global variable. - instr.(StringConstantInstruction).getIRVariable() = var and - bitOffset = 0 - or - exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset | - operand = instr.getAnOperand() and - // If an operand is propagated, then the result points to the same variable, - // offset by the bit offset from the propagation. - resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and - ( - operandIsPropagated(operand, propagatedBitOffset) - or - exists(CallInstruction ci, Instruction init | - isArgumentForParameter(ci, operand, init) and - resultReturned(init, propagatedBitOffset) - ) - ) and - bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) + exists(CallInstruction call, Instruction init | + isArgumentForParameter(call, operand, init) and + resultReturned(init, bitOffset) + ) +} + +/** + * Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset + * may be `unknown()`. + */ +private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) { + base = addrOperand.getDef() and bitOffset = 0 // Base case + or + exists( + Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset + | + // We already have an offset from `middle`. + hasBaseAndOffset(addrOperand, middle, previousBitOffset) and + // `middle` is propagated from `base`. + middleOperand = middle.getAnOperand() and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + base = middleOperand.getDef() and + bitOffset = Ints::add(previousBitOffset, additionalBitOffset) + ) +} + +/** + * Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`. + * Only holds for the `base` with the longest chain of propagation to `addrOperand`. + */ +predicate addressOperandBaseAndConstantOffset( + AddressOperand addrOperand, Instruction base, int bitOffset +) { + hasBaseAndOffset(addrOperand, base, bitOffset) and + Ints::hasValue(bitOffset) and + not exists(Instruction previousBase, int previousBitOffset | + hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and + previousBase = base.getAnOperand().getDef() and + Ints::hasValue(previousBitOffset) + ) +} + +/** + * Gets the allocation into which `addrOperand` points, if known. + */ +Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) { + addressOperandAllocationAndOffset(addrOperand, result, _) +} + +/** + * Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The + * offset may be `unknown()`. + */ +predicate addressOperandAllocationAndOffset( + AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset +) { + exists(Instruction base | + allocation.getABaseInstruction() = base and + hasBaseAndOffset(addrOperand, base, bitOffset) and + not exists(Instruction previousBase | + hasBaseAndOffset(addrOperand, previousBase, _) and + previousBase = base.getAnOperand().getDef() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll new file mode 100644 index 00000000000..c4aeaf93cce --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ir.implementation.IRConfiguration +import semmle.code.cpp.ir.internal.IntegerConstant as Ints +import semmle.code.cpp.models.interfaces.Alias as AliasModels diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisInternal.qll index 003b7008619..8a407105080 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisInternal.qll @@ -1 +1,3 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR +import AliasConfiguration as Configuration diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll new file mode 100644 index 00000000000..d9937294d70 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -0,0 +1,97 @@ +private import AliasConfigurationInternal +private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR +private import cpp +private import AliasAnalysis + +private newtype TAllocation = + TVariableAllocation(IRVariable var) or + TIndirectParameterAllocation(IRAutomaticUserVariable var) { + exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var) + } + +/** + * A memory allocation that can be tracked by the AliasedSSA alias analysis. + */ +abstract class Allocation extends TAllocation { + abstract string toString(); + + final string getAllocationString() { result = toString() } + + abstract Instruction getABaseInstruction(); + + abstract IRFunction getEnclosingIRFunction(); + + abstract Language::Location getLocation(); + + abstract string getUniqueId(); + + abstract IRType getIRType(); + + abstract predicate isReadOnly(); + + abstract predicate alwaysEscapes(); + + abstract predicate isAlwaysAllocatedOnStack(); + + final predicate isUnaliased() { not allocationEscapes(this) } +} + +class VariableAllocation extends Allocation, TVariableAllocation { + IRVariable var; + + VariableAllocation() { this = TVariableAllocation(var) } + + final override string toString() { result = var.toString() } + + final override VariableInstruction getABaseInstruction() { + result.getIRVariable() = var and + (result instanceof VariableAddressInstruction or result instanceof StringConstantInstruction) + } + + final override IRFunction getEnclosingIRFunction() { result = var.getEnclosingIRFunction() } + + final override Language::Location getLocation() { result = var.getLocation() } + + final override string getUniqueId() { result = var.getUniqueId() } + + final override IRType getIRType() { result = var.getIRType() } + + final override predicate isReadOnly() { var.isReadOnly() } + + final override predicate isAlwaysAllocatedOnStack() { var instanceof IRAutomaticVariable } + + final override predicate alwaysEscapes() { + // All variables with static storage duration have their address escape, even when escape analysis + // is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global + // variable. Normally, we rely on `AliasedDefinition` to handle that. + not var instanceof IRAutomaticVariable + } + + final IRVariable getIRVariable() { result = var } +} + +class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocation { + IRAutomaticUserVariable var; + + IndirectParameterAllocation() { this = TIndirectParameterAllocation(var) } + + final override string toString() { result = "*" + var.toString() } + + final override InitializeParameterInstruction getABaseInstruction() { + result.getIRVariable() = var + } + + final override IRFunction getEnclosingIRFunction() { result = var.getEnclosingIRFunction() } + + final override Language::Location getLocation() { result = var.getLocation() } + + final override string getUniqueId() { result = var.getUniqueId() } + + final override IRType getIRType() { result = var.getIRType() } + + final override predicate isReadOnly() { none() } + + final override predicate isAlwaysAllocatedOnStack() { none() } + + final override predicate alwaysEscapes() { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfigurationInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfigurationInternal.qll new file mode 100644 index 00000000000..bd6c2f4c151 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfigurationInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language 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 73bf062c88d..bcf3e5db19e 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 @@ -6,38 +6,52 @@ 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.implementation.internal.OperandTag +private import AliasConfiguration private class IntValue = Ints::IntValue; +private predicate isIndirectOrBufferMemoryAccess(MemoryAccessKind kind) { + kind instanceof IndirectMemoryAccess or + kind instanceof BufferMemoryAccess +} + private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType, + Instruction instr, Allocation var, IRType type, Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess ) { - resultPointsTo(instr.getResultAddress(), var, startBitOffset) and - languageType = instr.getResultLanguageType() and - type = languageType.getIRType() and - (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and - if exists(type.getByteSize()) - then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) - else endBitOffset = Ints::unknown() + exists(AddressOperand addrOperand | + addrOperand = instr.getResultAddressOperand() and + addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and + languageType = instr.getResultLanguageType() and + type = languageType.getIRType() and + isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and + (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and + if exists(type.getByteSize()) + then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) + else endBitOffset = Ints::unknown() + ) } private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType, + MemoryOperand operand, Allocation var, IRType type, Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess ) { - resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and - languageType = operand.getLanguageType() and - type = languageType.getIRType() and - (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and - if exists(type.getByteSize()) - then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) - else endBitOffset = Ints::unknown() + exists(AddressOperand addrOperand | + addrOperand = operand.getAddressOperand() and + addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and + languageType = operand.getLanguageType() and + type = languageType.getIRType() and + isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and + (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and + if exists(type.getByteSize()) + then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) + else endBitOffset = Ints::unknown() + ) } private newtype TMemoryLocation = TVariableMemoryLocation( - IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset, + Allocation var, IRType type, Language::LanguageType languageType, IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess ) { ( @@ -45,17 +59,18 @@ private newtype TMemoryLocation = or hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess) or - exists(IRAutomaticVariable autoVar | - // Always create a memory location for the entire variable. - autoVar = var and - type = autoVar.getIRType() and - startBitOffset = 0 and - endBitOffset = type.getByteSize() * 8 and - isMayAccess = false - ) + // For a stack variable, always create a memory location for the entire variable. + var.isAlwaysAllocatedOnStack() and + type = var.getIRType() and + startBitOffset = 0 and + endBitOffset = type.getByteSize() * 8 and + isMayAccess = false ) and languageType = type.getCanonicalLanguageType() } or + TEntireAllocationMemoryLocation(IndirectParameterAllocation var, boolean isMayAccess) { + isMayAccess = false or isMayAccess = true + } or TUnknownMemoryLocation(IRFunction irFunc, boolean isMayAccess) { isMayAccess = false or isMayAccess = true } or @@ -94,6 +109,8 @@ abstract class MemoryLocation extends TMemoryLocation { abstract predicate isMayAccess(); + Allocation getAllocation() { none() } + /** * Holds if the location cannot be overwritten except by definition of a `MemoryLocation` for * which `def.canDefineReadOnly()` holds. @@ -114,27 +131,63 @@ abstract class MemoryLocation extends TMemoryLocation { abstract class VirtualVariable extends MemoryLocation { } +abstract class AllocationMemoryLocation extends MemoryLocation { + Allocation var; + boolean isMayAccess; + + AllocationMemoryLocation() { + this instanceof TMemoryLocation and + isMayAccess = false + or + isMayAccess = true // Just ensures that `isMayAccess` is bound. + } + + final override VirtualVariable getVirtualVariable() { + if allocationEscapes(var) + then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false) + else result.(AllocationMemoryLocation).getAllocation() = var + } + + final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() } + + final override Location getLocation() { result = var.getLocation() } + + final override Allocation getAllocation() { result = var } + + final override predicate isMayAccess() { isMayAccess = true } + + final override predicate isReadOnly() { var.isReadOnly() } +} + /** * 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 (assigned to `UnknownVirtualVariable`). */ -class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { - IRVariable var; +class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLocation { IRType type; Language::LanguageType languageType; IntValue startBitOffset; IntValue endBitOffset; - boolean isMayAccess; VariableMemoryLocation() { this = TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset, isMayAccess) } + private string getIntervalString() { + if coversEntireVariable() + then result = "" + else result = Interval::getIntervalString(startBitOffset, endBitOffset) + } + + private string getTypeString() { + if coversEntireVariable() and type = var.getIRType() + then result = "" + else result = "<" + languageType.toString() + ">" + } + final override string toStringInternal() { - result = - var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" + - type.toString() + ", " + languageType.toString() + ">" + result = var.toString() + getIntervalString() + getTypeString() } final override Language::LanguageType getType() { @@ -152,35 +205,17 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { result = type.getCanonicalLanguageType() } - final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() } - - final override Location getLocation() { result = var.getLocation() } - final IntValue getStartBitOffset() { result = startBitOffset } final IntValue getEndBitOffset() { result = endBitOffset } - final IRVariable getVariable() { result = var } - final override string getUniqueId() { result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" + type.getIdentityString() + ">" } - final override VirtualVariable getVirtualVariable() { - if variableAddressEscapes(var) - then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false) - else - result = - TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8, false) - } - - final override predicate isMayAccess() { isMayAccess = true } - - final override predicate isReadOnly() { var.isReadOnly() } - - final override predicate isAlwaysAllocatedOnStack() { var instanceof IRAutomaticVariable } + final override predicate isAlwaysAllocatedOnStack() { var.isAlwaysAllocatedOnStack() } /** * Holds if this memory location covers the entire variable. @@ -191,6 +226,26 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { } } +class EntireAllocationMemoryLocation extends TEntireAllocationMemoryLocation, + AllocationMemoryLocation { + EntireAllocationMemoryLocation() { this = TEntireAllocationMemoryLocation(var, isMayAccess) } + + final override string toStringInternal() { result = var.toString() } + + final override Language::LanguageType getType() { + result = any(IRUnknownType unknownType).getCanonicalLanguageType() + } + + final override string getUniqueId() { result = var.getUniqueId() } +} + +class EntireAllocationVirtualVariable extends EntireAllocationMemoryLocation, VirtualVariable { + EntireAllocationVirtualVariable() { + not allocationEscapes(var) and + not isMayAccess() + } +} + /** * Represents the `MemoryLocation` for an `IRVariable` that acts as its own `VirtualVariable`. Includes any * `VariableMemoryLocation` that exactly overlaps its entire `IRVariable`, and only if that `IRVariable` does not @@ -198,7 +253,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { */ class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable { VariableVirtualVariable() { - not variableAddressEscapes(var) and + not allocationEscapes(var) and type = var.getIRType() and coversEntireVariable() and not isMayAccess() @@ -356,13 +411,30 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) { not use.isAlwaysAllocatedOnStack() ) or + def.getVirtualVariable() = use.getVirtualVariable() and + def instanceof EntireAllocationMemoryLocation and + ( + // EntireAllocationMemoryLocation exactly overlaps itself. + use instanceof EntireAllocationMemoryLocation and + result instanceof MustExactlyOverlap + or + // EntireAllocationMemoryLocation totally overlaps any location within the same virtual + // variable. + not use instanceof EntireAllocationMemoryLocation and + result instanceof MustTotallyOverlap + ) + or exists(VariableMemoryLocation defVariableLocation | defVariableLocation = def and ( // A VariableMemoryLocation may partially overlap an unknown location within the same // virtual variable. def.getVirtualVariable() = use.getVirtualVariable() and - (use instanceof UnknownMemoryLocation or use instanceof AllAliasedMemory) and + ( + use instanceof UnknownMemoryLocation or + use instanceof AllAliasedMemory or + use instanceof EntireAllocationMemoryLocation + ) and result instanceof MayPartiallyOverlap or // A VariableMemoryLocation that is not a local variable may partially overlap an @@ -423,19 +495,19 @@ private predicate isRelatableMemoryLocation(VariableMemoryLocation vml) { vml.getStartBitOffset() != Ints::unknown() } -private predicate isCoveredOffset(IRVariable var, int offsetRank, VariableMemoryLocation vml) { +private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemoryLocation vml) { exists(int startRank, int endRank, VirtualVariable vvar | vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and vml.getEndBitOffset() = rank[endRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and - var = vml.getVariable() and + var = vml.getAllocation() and vvar = vml.getVirtualVariable() and isRelatableMemoryLocation(vml) and offsetRank in [startRank .. endRank] ) } -private predicate hasUnknownOffset(IRVariable var, VariableMemoryLocation vml) { - vml.getVariable() = var and +private predicate hasUnknownOffset(Allocation var, VariableMemoryLocation vml) { + vml.getAllocation() = var and ( vml.getStartBitOffset() = Ints::unknown() or vml.getEndBitOffset() = Ints::unknown() @@ -445,14 +517,14 @@ private predicate hasUnknownOffset(IRVariable var, VariableMemoryLocation vml) { private predicate overlappingIRVariableMemoryLocations( VariableMemoryLocation def, VariableMemoryLocation use ) { - exists(IRVariable var, int offsetRank | + exists(Allocation var, int offsetRank | isCoveredOffset(var, offsetRank, def) and isCoveredOffset(var, offsetRank, use) ) or - hasUnknownOffset(use.getVariable(), def) + hasUnknownOffset(use.getAllocation(), def) or - hasUnknownOffset(def.getVariable(), use) + hasUnknownOffset(def.getAllocation(), use) } private Overlap getVariableMemoryLocationOverlap( @@ -470,10 +542,10 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and ( ( - kind.usesAddressOperand() and + isIndirectOrBufferMemoryAccess(kind) and if hasResultMemoryAccess(instr, _, _, _, _, _, _) then - exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset | + exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset | hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess) @@ -481,6 +553,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess) ) or + kind instanceof EntireAllocationMemoryAccess and + result = + TEntireAllocationMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()), + isMayAccess) + or kind instanceof EscapedMemoryAccess and result = TAllAliasedMemory(instr.getEnclosingIRFunction(), isMayAccess) or @@ -496,10 +573,10 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and ( ( - kind.usesAddressOperand() and + isIndirectOrBufferMemoryAccess(kind) and if hasOperandMemoryAccess(operand, _, _, _, _, _, _) then - exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset | + exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset | hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset, isMayAccess) and result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess) @@ -507,6 +584,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess) ) or + kind instanceof EntireAllocationMemoryAccess and + result = + TEntireAllocationMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand()), + isMayAccess) + or kind instanceof EscapedMemoryAccess and result = TAllAliasedMemory(operand.getEnclosingIRFunction(), isMayAccess) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index ff4602347ae..e2d3828fc52 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -1,21 +1,9 @@ private import AliasAnalysisInternal -private import cpp private import InputIR -private import semmle.code.cpp.ir.internal.IntegerConstant as Ints -private import semmle.code.cpp.ir.implementation.IRConfiguration -private import semmle.code.cpp.models.interfaces.Alias +private import AliasAnalysisImports private class IntValue = Ints::IntValue; -/** - * Gets the offset of field `field` in bits. - */ -private IntValue getFieldBitOffset(Field field) { - if field instanceof BitField - then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset()) - else result = Ints::mul(field.getByteOffset(), 8) -} - /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -36,7 +24,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { instr instanceof PointerDiffInstruction or // Converting an address to a `bool` does not escape the address. - instr.(ConvertInstruction).getResultType() instanceof BoolType + instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType ) ) or @@ -111,13 +99,10 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() or // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, Type resultType | + exists(ConvertInstruction convert, IRType resultType | convert = instr and - resultType = convert.getResultType() and - ( - resultType instanceof PointerType or - resultType instanceof Class //REVIEW: Remove when all glvalues are pointers - ) and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and bitOffset = 0 ) or @@ -131,7 +116,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { or // Computing a field address from a pointer propagates the address plus the // offset of the field. - bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or // A copy propagates the source value. operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 @@ -212,7 +197,7 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { } private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { - exists(Function f | + exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( @@ -223,27 +208,27 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In init.getEnclosingFunction() = f and operand instanceof ThisArgumentOperand ) and - not f.isVirtual() and - not f instanceof AliasFunction + not Language::isFunctionVirtual(f) and + not f instanceof AliasModels::AliasFunction ) } private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) ) } private predicate isOnlyEscapesViaReturnArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) ) } private predicate isNeverEscapesArgument(Operand operand) { - exists(AliasFunction f | + exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) ) @@ -265,61 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) { } /** - * Holds if the address of the specified local variable or parameter escapes the - * domain of the analysis. + * Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur + * either because the allocation's address is taken within the function and escapes, or because the + * allocation is marked as always escaping via `alwaysEscapes()`. */ -private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { - // The variable's address escapes if the result of any - // VariableAddressInstruction that computes the variable's address escapes. - exists(VariableAddressInstruction instr | - instr.getIRVariable() = var and - resultEscapesNonReturn(instr) - ) -} - -/** - * Holds if the address of the specified variable escapes the domain of the - * analysis. - */ -predicate variableAddressEscapes(IRVariable var) { +predicate allocationEscapes(Configuration::Allocation allocation) { + allocation.alwaysEscapes() + or exists(IREscapeAnalysisConfiguration config | - config.useSoundEscapeAnalysis() and - automaticVariableAddressEscapes(var.(IRAutomaticVariable)) + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) - or - // All variables with static storage duration have their address escape, even when escape analysis - // is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global - // variable. Normally, we rely on `AliasedDefinition` to handle that. - not var instanceof IRAutomaticVariable } /** - * Holds if the result of instruction `instr` points within variable `var`, at - * bit offset `bitOffset` within the variable. If the result points within - * `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown. + * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) { - // The address of a variable points to that variable, at offset 0. - instr.(VariableAddressInstruction).getIRVariable() = var and - bitOffset = 0 +private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { + operandIsPropagated(operand, bitOffset) or - // A string literal is just a special read-only global variable. - instr.(StringConstantInstruction).getIRVariable() = var and - bitOffset = 0 - or - exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset | - operand = instr.getAnOperand() and - // If an operand is propagated, then the result points to the same variable, - // offset by the bit offset from the propagation. - resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and - ( - operandIsPropagated(operand, propagatedBitOffset) - or - exists(CallInstruction ci, Instruction init | - isArgumentForParameter(ci, operand, init) and - resultReturned(init, propagatedBitOffset) - ) - ) and - bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) + exists(CallInstruction call, Instruction init | + isArgumentForParameter(call, operand, init) and + resultReturned(init, bitOffset) + ) +} + +/** + * Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset + * may be `unknown()`. + */ +private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) { + base = addrOperand.getDef() and bitOffset = 0 // Base case + or + exists( + Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset + | + // We already have an offset from `middle`. + hasBaseAndOffset(addrOperand, middle, previousBitOffset) and + // `middle` is propagated from `base`. + middleOperand = middle.getAnOperand() and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + base = middleOperand.getDef() and + bitOffset = Ints::add(previousBitOffset, additionalBitOffset) + ) +} + +/** + * Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`. + * Only holds for the `base` with the longest chain of propagation to `addrOperand`. + */ +predicate addressOperandBaseAndConstantOffset( + AddressOperand addrOperand, Instruction base, int bitOffset +) { + hasBaseAndOffset(addrOperand, base, bitOffset) and + Ints::hasValue(bitOffset) and + not exists(Instruction previousBase, int previousBitOffset | + hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and + previousBase = base.getAnOperand().getDef() and + Ints::hasValue(previousBitOffset) + ) +} + +/** + * Gets the allocation into which `addrOperand` points, if known. + */ +Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) { + addressOperandAllocationAndOffset(addrOperand, result, _) +} + +/** + * Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The + * offset may be `unknown()`. + */ +predicate addressOperandAllocationAndOffset( + AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset +) { + exists(Instruction base | + allocation.getABaseInstruction() = base and + hasBaseAndOffset(addrOperand, base, bitOffset) and + not exists(Instruction previousBase | + hasBaseAndOffset(addrOperand, previousBase, _) and + previousBase = base.getAnOperand().getDef() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll new file mode 100644 index 00000000000..c4aeaf93cce --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ir.implementation.IRConfiguration +import semmle.code.cpp.ir.internal.IntegerConstant as Ints +import semmle.code.cpp.models.interfaces.Alias as AliasModels diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll index 8a9e43e14a3..08a563abc73 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll @@ -1 +1,3 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language import semmle.code.cpp.ir.implementation.raw.IR as InputIR +import AliasConfiguration as Configuration diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll new file mode 100644 index 00000000000..5be476e12ee --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -0,0 +1,16 @@ +private import AliasConfigurationImports + +/** + * A memory allocation that can be tracked by the SimpleSSA alias analysis. + * All automatic variables are tracked. + */ +class Allocation extends IRAutomaticVariable { + VariableAddressInstruction getABaseInstruction() { result.getIRVariable() = this } + + final string getAllocationString() { result = toString() } + + predicate alwaysEscapes() { + // An automatic variable only escapes if its address is taken and escapes. + none() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll new file mode 100644 index 00000000000..07cbc6308b7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.raw.IR 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 ddff6444b90..7de1ab8d72e 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,66 +1,57 @@ import AliasAnalysis private import SimpleSSAImports import SimpleSSAPublicImports +private import AliasConfiguration -private class IntValue = Ints::IntValue; - -private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset -) { - resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and - type = instr.getResultLanguageType() -} - -private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset -) { - resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and - type = operand.getLanguageType() -} - -/** - * 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, Language::LanguageType type, IntValue bitOffset | - hasResultMemoryAccess(instr, var, type, bitOffset) and - not instr.hasResultMayMemoryAccess() - | +private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRType type) { + exists(Instruction constantBase, int bitOffset | + addressOperandBaseAndConstantOffset(addrOperand, constantBase, bitOffset) and bitOffset = 0 and - type.getIRType() = var.getIRType() and - not instr.hasResultMayMemoryAccess() - ) and - forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset | - hasOperandMemoryAccess(operand, var, type, bitOffset) - | - bitOffset = 0 and - type.getIRType() = var.getIRType() and - not operand.hasMayReadMemoryAccess() + constantBase = var.getABaseInstruction() and + type = var.getIRType() ) } -private newtype TMemoryLocation = MkMemoryLocation(IRVariable var) { isVariableModeled(var) } +/** + * 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(Allocation var) { + not allocationEscapes(var) and + forall(Instruction instr, AddressOperand addrOperand, IRType type | + addrOperand = instr.getResultAddressOperand() and + type = instr.getResultIRType() and + var = getAddressOperandAllocation(addrOperand) + | + isTotalAccess(var, addrOperand, type) and not instr.hasResultMayMemoryAccess() + ) and + forall(MemoryOperand memOperand, AddressOperand addrOperand, IRType type | + addrOperand = memOperand.getAddressOperand() and + type = memOperand.getIRType() and + var = getAddressOperandAllocation(addrOperand) + | + isTotalAccess(var, addrOperand, type) and not memOperand.hasMayReadMemoryAccess() + ) +} -private MemoryLocation getMemoryLocation(IRVariable var) { result.getIRVariable() = var } +private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } + +private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } class MemoryLocation extends TMemoryLocation { - IRVariable var; + Allocation var; MemoryLocation() { this = MkMemoryLocation(var) } - final string toString() { result = var.toString() } + final string toString() { result = var.getAllocationString() } + + final Allocation getAllocation() { result = var } final Language::Location getLocation() { result = var.getLocation() } final IRFunction getIRFunction() { result = var.getEnclosingIRFunction() } - final IRVariable getIRVariable() { result = var } - final VirtualVariable getVirtualVariable() { result = this } final Language::LanguageType getType() { result = var.getLanguageType() } @@ -77,15 +68,9 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) { } MemoryLocation getResultMemoryLocation(Instruction instr) { - exists(IRVariable var | - hasResultMemoryAccess(instr, var, _, _) and - result = getMemoryLocation(var) - ) + result = getMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand())) } MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { - exists(IRVariable var | - hasOperandMemoryAccess(operand, var, _, _) and - result = getMemoryLocation(var) - ) + result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand())) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll index 2adea27afb4..3bde6c7d501 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll @@ -80,3 +80,17 @@ predicate hasPotentialLoop(Function f) { } predicate hasGoto(Function f) { exists(Cpp::GotoStmt s | s.getEnclosingFunction() = f) } + +/** + * Gets the offset of field `field` in bits. + */ +int getFieldBitOffset(Field field) { + if field instanceof Cpp::BitField + then result = (field.getByteOffset() * 8) + field.(Cpp::BitField).getBitOffset() + else result = field.getByteOffset() * 8 +} + +/** + * Holds if the specified `Function` can be overridden in a derived class. + */ +predicate isFunctionVirtual(Function f) { f.isVirtual() } diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll new file mode 100644 index 00000000000..bbd9090c8e6 --- /dev/null +++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -0,0 +1,285 @@ +/** + * Provides a library for writing QL tests whose success or failure is based on expected results + * embedded in the test source code as comments, rather than a `.expected` file. + * + * To create a new inline expectations test: + * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the + * new class, bind `this` to a unique string (usually the name of the test). + * - Override the `hasActualResult()` predicate to produce the actual results of the query. For each + * result, specify a `Location`, a text description of the element for which the result was + * reported, a short string to serve as the tag to identify expected results for this test, and the + * expected value of the result. + * - Override `getARelevantTag()` to return the set of tags that can be produced by + * `hasActualResult()`. Often this is just a single tag. + * + * Example: + * ``` + * class ConstantValueTest extends InlineExpectationsTest { + * ConstantValueTest() { this = "ConstantValueTest" } + * + * override string getARelevantTag() { + * // We only use one tag for this test. + * result = "const" + * } + * + * override predicate hasActualResult( + * Location location, string element, string tag, string valuesasas + * ) { + * exists(Expr e | + * tag = "const" and // The tag for this test. + * valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions. + * location = e.getLocation() and // The location of the result to be reported. + * element = e.toString() // The display text for the result. + * ) + * } + * } + * ``` + * + * There is no need to write a `select` clause or query predicate. All of the differences between + * expected results and actual results will be reported in the `failures()` query predicate. + * + * To annotate the test source code with an expected result, place a C++-style (`//`) comment on the + * same line as the expected result, with text of the following format as the body of the comment: + * + * `// $tag=expected-value` + * + * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is + * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be + * omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may + * be placed in the same comment, as long as each is prefixed by a `$`. Any actual result that + * appears on a line that does not contain a matching expected result comment will be reported with + * a message of the form "Unexpected result: tag=value". Any expected result comment for which there + * is no matching actual result will be reported with a message of the form + * "Missing result: tag=expected-value". + * + * Example: + * ``` + * int i = x + 5; // $const=5 + * int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant. + * ``` + * + * For tests that contain known false positives and false negatives, it is possible to further + * annotate that a particular expected result is known to be a false positive, or that a particular + * missing result is known to be a false negative: + * + * `// $f+:tag=expected-value` // False positive + * `// $f-:tag=expected-value` // False negative + * + * A false positive expectation is treated as any other expected result, except that if there is no + * matching actual result, the message will be of the form "Fixed false positive: tag=value". A + * false negative expectation is treated as if there were no expected result, except that if a + * matching expected result is found, the message will be of the form + * "Fixed false negative: tag=value". + * + * If the same result value is expected for two or more tags on the same line, there is a shorthand + * notation available: + * + * `// $tag1,tag2=expected-value` + * + * is equivalent to: + * + * `// $tag1=expected-value $tag2=expected-value` + */ + +import cpp + +/** + * Base class for tests with inline expectations. The test extends this class to provide the actual + * results of the query, which are then compared with the expected results in comments to produce a + * list of failure messages that point out where the actual results differ from the expected + * results. + */ +abstract class InlineExpectationsTest extends string { + bindingset[this] + InlineExpectationsTest() { any() } + + /** + * Returns all tags that can be generated by this test. Most tests will only ever produce a single + * tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()` + * predicate for an active test will be ignored. This makes it possible to write multiple tests in + * different `.ql` files that all query the same source code. + */ + abstract string getARelevantTag(); + + /** + * Returns the actual results of the query that is being tested. Each result consist of the + * following values: + * - `location` - The source code location of the result. Any expected result comment must appear + * on the start line of this location. + * - `element` - Display text for the element on which the result is reported. + * - `tag` - The tag that marks this result as coming from this test. This must be one of the tags + * returned by `getARelevantTag()`. + * - `value` - The value of the result, which will be matched against the value associated with + * `tag` in any expected result comment on that line. + */ + abstract predicate hasActualResult(Location location, string element, string tag, string value); + + final predicate hasFailureMessage(FailureLocatable element, string message) { + exists(ActualResult actualResult | + actualResult.getTest() = this and + element = actualResult and + ( + exists(FalseNegativeExpectation falseNegative | + falseNegative.matchesActualResult(actualResult) and + message = "Fixed false negative:" + falseNegative.getExpectationText() + ) + or + not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and + message = "Unexpected result: " + actualResult.getExpectationText() + ) + ) + or + exists(ValidExpectation expectation | + not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and + expectation.getTag() = getARelevantTag() and + element = expectation and + ( + expectation instanceof GoodExpectation and + message = "Missing result:" + expectation.getExpectationText() + or + expectation instanceof FalsePositiveExpectation and + message = "Fixed false positive:" + expectation.getExpectationText() + ) + ) + or + exists(InvalidExpectation expectation | + element = expectation and + message = "Invalid expectation syntax: " + expectation.getExpectation() + ) + } +} + +/** + * RegEx pattern to match a comment containing one or more expected results. The comment must be a + * C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character + * is treated as part of the expected results, except that the comment may contain a `//` sequence + * to treat the remainder of the line as a regular (non-interpreted) comment. + */ +private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" } + +/** + * RegEx pattern to match a single expected result, not including the leading `$`. It starts with an + * optional `f+:` or `f-:`, followed by one or more comma-separated tags containing only letters, + * `-`, and `_`, optionally followed by `=` and the expected value. + */ +private string expectationPattern() { + result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?" +} + +private string getAnExpectation(CppStyleComment comment) { + result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and + result != "" +} + +private newtype TFailureLocatable = + TActualResult( + InlineExpectationsTest test, Location location, string element, string tag, string value + ) { + test.hasActualResult(location, element, tag, value) + } or + TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) { + exists(string expectation | + expectation = getAnExpectation(comment) and + expectation.regexpMatch(expectationPattern()) and + tag = expectation.regexpCapture(expectationPattern(), 2).splitAt(",").trim() and + ( + if exists(expectation.regexpCapture(expectationPattern(), 3)) + then value = expectation.regexpCapture(expectationPattern(), 3) + else value = "" + ) and + ( + if exists(expectation.regexpCapture(expectationPattern(), 1)) + then knownFailure = expectation.regexpCapture(expectationPattern(), 1) + else knownFailure = "" + ) + ) + } or + TInvalidExpectation(CppStyleComment comment, string expectation) { + expectation = getAnExpectation(comment) and + not expectation.regexpMatch(expectationPattern()) + } + +class FailureLocatable extends TFailureLocatable { + string toString() { none() } + + Location getLocation() { none() } + + final string getExpectationText() { result = getTag() + "=" + getValue() } + + string getTag() { none() } + + string getValue() { none() } +} + +class ActualResult extends FailureLocatable, TActualResult { + InlineExpectationsTest test; + Location location; + string element; + string tag; + string value; + + ActualResult() { this = TActualResult(test, location, element, tag, value) } + + override string toString() { result = element } + + override Location getLocation() { result = location } + + InlineExpectationsTest getTest() { result = test } + + override string getTag() { result = tag } + + override string getValue() { result = value } +} + +abstract private class Expectation extends FailureLocatable { + CppStyleComment comment; + + override string toString() { result = comment.toString() } + + override Location getLocation() { result = comment.getLocation() } +} + +private class ValidExpectation extends Expectation, TValidExpectation { + string tag; + string value; + string knownFailure; + + ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + string getKnownFailure() { result = knownFailure } + + predicate matchesActualResult(ActualResult actualResult) { + getLocation().getStartLine() = actualResult.getLocation().getStartLine() and + getLocation().getFile() = actualResult.getLocation().getFile() and + getTag() = actualResult.getTag() and + getValue() = actualResult.getValue() + } +} + +class GoodExpectation extends ValidExpectation { + GoodExpectation() { getKnownFailure() = "" } +} + +class FalsePositiveExpectation extends ValidExpectation { + FalsePositiveExpectation() { getKnownFailure() = "f+" } +} + +class FalseNegativeExpectation extends ValidExpectation { + FalseNegativeExpectation() { getKnownFailure() = "f-" } +} + +class InvalidExpectation extends Expectation, TInvalidExpectation { + string expectation; + + InvalidExpectation() { this = TInvalidExpectation(comment, expectation) } + + string getExpectation() { result = expectation } +} + +query predicate failures(FailureLocatable element, string message) { + exists(InlineExpectationsTest test | test.hasFailureMessage(element, message)) +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp index 9c3c8bc4569..f2b91c00ed0 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp @@ -61,8 +61,8 @@ void bg_structptr(XY *p1, XY *p2) { if (guarded(p1->x)) { sink(p1->x); // no flow [FALSE POSITIVE in AST] } else if (guarded(p1->y)) { - sink(p1->x); // flow [NOT DETECTED in IR] + sink(p1->x); // flow } else if (guarded(p2->x)) { - sink(p1->x); // flow [NOT DETECTED in IR] + sink(p1->x); // flow } } 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 e40505722af..31639764ad6 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,8 +1,5 @@ | BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only | -| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:64:14:64:14 | AST only | -| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:66:14:66:14 | AST only | | clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only | -| clang.cpp:28:27:28:32 | clang.cpp:29:27:29:28 | AST only | | clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only | | clang.cpp:39:42:39:47 | clang.cpp:41:18:41:19 | IR only | | dispatch.cpp:16:37:16:42 | dispatch.cpp:32:16:32:24 | 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 0e67184f477..bb20ccfc9d7 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 @@ -5,10 +5,13 @@ | BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source | | BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source | | BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source | +| BarrierGuard.cpp:64:14:64:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source | +| BarrierGuard.cpp:66:14:66:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source | | acrossLinkTargets.cpp:12:8:12:8 | (int)... | acrossLinkTargets.cpp:19:27:19:32 | call to source | | acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source | | clang.cpp:18:8:18:19 | (const int *)... | clang.cpp:12:9:12:20 | sourceArray1 | | clang.cpp:18:8:18:19 | sourceArray1 | clang.cpp:12:9:12:20 | sourceArray1 | +| clang.cpp:29:27:29:28 | m1 | clang.cpp:28:27:28:32 | call to source | | clang.cpp:37:10:37:11 | m2 | clang.cpp:34:32:34:37 | call to source | | clang.cpp:41:18:41:19 | m2 | clang.cpp:39:42:39:47 | call to source | | clang.cpp:45:17:45:18 | m2 | clang.cpp:43:35:43:40 | call to source | diff --git a/cpp/ql/test/library-tests/ir/escape/escape.ql b/cpp/ql/test/library-tests/ir/escape/escape.ql index 109ff260b7d..9099fea159e 100644 --- a/cpp/ql/test/library-tests/ir/escape/escape.ql +++ b/cpp/ql/test/library-tests/ir/escape/escape.ql @@ -16,9 +16,9 @@ where exists(IRFunction irFunc | irFunc = var.getEnclosingIRFunction() and ( - shouldEscape(var) and variableAddressEscapes(var) + shouldEscape(var) and allocationEscapes(var) or - not shouldEscape(var) and not variableAddressEscapes(var) + not shouldEscape(var) and not allocationEscapes(var) ) ) select var diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.expected b/cpp/ql/test/library-tests/ir/escape/points_to.expected deleted file mode 100644 index 6447bd81a5f..00000000000 --- a/cpp/ql/test/library-tests/ir/escape/points_to.expected +++ /dev/null @@ -1,86 +0,0 @@ -| escape.cpp:111:18:111:21 | CopyValue | no_+0 | no_+0 | -| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0 | no_+0 | -| escape.cpp:115:20:115:23 | CopyValue | no_+0 | no_+0 | -| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0 | no_+0 | -| escape.cpp:116:20:116:23 | CopyValue | no_+0 | no_+0 | -| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0 | no_+0 | -| escape.cpp:117:23:117:26 | CopyValue | no_+0 | no_+0 | -| escape.cpp:118:9:118:12 | CopyValue | no_+0 | no_+0 | -| escape.cpp:120:12:120:15 | CopyValue | no_+0 | no_+0 | -| escape.cpp:123:14:123:17 | CopyValue | no_+0 | no_+0 | -| escape.cpp:124:15:124:18 | CopyValue | no_+0 | no_+0 | -| escape.cpp:127:9:127:12 | CopyValue | no_+0 | no_+0 | -| escape.cpp:129:12:129:15 | CopyValue | no_+0 | no_+0 | -| escape.cpp:134:5:134:18 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:134:11:134:18 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:135:5:135:12 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:135:5:135:15 | PointerAdd[4] | no_Array+20 | no_Array+20 | -| escape.cpp:136:5:136:15 | PointerAdd[4] | no_Array+20 | no_Array+20 | -| escape.cpp:136:7:136:14 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:137:17:137:24 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:137:17:137:27 | PointerAdd[4] | no_Array+20 | no_Array+20 | -| escape.cpp:138:17:138:27 | PointerAdd[4] | no_Array+20 | no_Array+20 | -| escape.cpp:138:19:138:26 | Convert | no_Array+0 | no_Array+0 | -| escape.cpp:140:21:140:32 | FieldAddress[x] | no_Point+0 | no_Point+0 | -| escape.cpp:140:21:140:32 | FieldAddress[y] | no_Point+4 | no_Point+4 | -| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8 | no_Point+8 | -| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0 | no_Point+0 | -| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4 | no_Point+4 | -| escape.cpp:143:19:143:27 | CopyValue | no_Point+0 | no_Point+0 | -| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4 | no_Point+4 | -| escape.cpp:144:6:144:14 | CopyValue | no_Point+0 | no_Point+0 | -| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4 | no_Point+4 | -| escape.cpp:145:20:145:30 | CopyValue | no_Point+8 | no_Point+8 | -| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8 | no_Point+8 | -| escape.cpp:146:5:146:18 | CopyValue | no_Point+8 | no_Point+8 | -| escape.cpp:146:7:146:17 | CopyValue | no_Point+8 | no_Point+8 | -| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8 | no_Point+8 | -| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0 | no_Derived+0 | -| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0 | no_Derived+0 | -| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0 | no_Derived+0 | -| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0 | no_Derived+0 | -| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0 | no_Derived+0 | -| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0 | no_Derived+0 | -| escape.cpp:151:5:151:14 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12 | no_Derived+12 | -| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16 | no_Derived+16 | -| escape.cpp:152:19:152:28 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12 | no_Derived+12 | -| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16 | no_Derived+16 | -| escape.cpp:155:17:155:30 | CopyValue | no_ssa_addrOf+0 | no_ssa_addrOf+0 | -| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0 | no_ssa_addrOf+0 | -| escape.cpp:158:17:158:28 | CopyValue | no_ssa_refTo+0 | no_ssa_refTo+0 | -| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0 | no_ssa_refTo+0 | -| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0 | no_ssa_refToArrayElement+0 | -| escape.cpp:161:19:161:45 | CopyValue | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 | -| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 | -| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 | -| escape.cpp:164:24:164:40 | CopyValue | no_ssa_refToArray+0 | no_ssa_refToArray+0 | -| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0 | no_ssa_refToArray+0 | -| escape.cpp:167:19:167:28 | CopyValue | passByPtr+0 | passByPtr+0 | -| escape.cpp:170:21:170:29 | CopyValue | passByRef+0 | passByRef+0 | -| escape.cpp:173:22:173:38 | CopyValue | no_ssa_passByPtr+0 | no_ssa_passByPtr+0 | -| escape.cpp:176:24:176:39 | CopyValue | no_ssa_passByRef+0 | no_ssa_passByRef+0 | -| escape.cpp:179:22:179:42 | CopyValue | no_ssa_passByPtr_ret+0 | no_ssa_passByPtr_ret+0 | -| escape.cpp:182:24:182:43 | CopyValue | no_ssa_passByRef_ret+0 | no_ssa_passByRef_ret+0 | -| escape.cpp:185:30:185:40 | CopyValue | passByPtr2+0 | passByPtr2+0 | -| escape.cpp:188:32:188:41 | CopyValue | passByRef2+0 | passByRef2+0 | -| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0 | -| escape.cpp:191:44:191:54 | CopyValue | passByPtr3+0 | passByPtr3+0 | -| escape.cpp:194:32:194:46 | Call | none | passByRef3+0 | -| escape.cpp:194:32:194:59 | CopyValue | none | passByRef3+0 | -| escape.cpp:194:48:194:57 | CopyValue | passByRef3+0 | passByRef3+0 | -| escape.cpp:199:17:199:34 | CopyValue | no_ssa_passByPtr4+0 | no_ssa_passByPtr4+0 | -| escape.cpp:199:37:199:54 | CopyValue | no_ssa_passByPtr5+0 | no_ssa_passByPtr5+0 | -| escape.cpp:202:5:202:19 | Call | none | passByRef6+0 | -| escape.cpp:202:5:202:32 | CopyValue | none | passByRef6+0 | -| escape.cpp:202:21:202:30 | CopyValue | passByRef6+0 | passByRef6+0 | -| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0 | -| escape.cpp:205:5:205:39 | CopyValue | none | no_ssa_passByRef7+0 | -| escape.cpp:205:21:205:37 | CopyValue | no_ssa_passByRef7+0 | no_ssa_passByRef7+0 | -| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0 | -| escape.cpp:217:14:217:16 | CopyValue | c2+0 | c2+0 | -| escape.cpp:221:8:221:19 | Call | none | c3+0 | -| escape.cpp:225:17:225:28 | Call | none | c4+0 | -| escape.cpp:247:2:247:27 | Store | condEscape1+0 | condEscape1+0 | -| escape.cpp:247:16:247:27 | CopyValue | condEscape1+0 | condEscape1+0 | -| escape.cpp:249:9:249:34 | Store | condEscape2+0 | condEscape2+0 | -| escape.cpp:249:23:249:34 | CopyValue | condEscape2+0 | condEscape2+0 | diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.ql b/cpp/ql/test/library-tests/ir/escape/points_to.ql deleted file mode 100644 index 7c265974b10..00000000000 --- a/cpp/ql/test/library-tests/ir/escape/points_to.ql +++ /dev/null @@ -1,35 +0,0 @@ -import default -import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis as RawAA -import semmle.code.cpp.ir.implementation.raw.IR as Raw -import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis as UnAA -import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Un -import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction -import semmle.code.cpp.ir.internal.IntegerConstant - -from Raw::Instruction rawInstr, Un::Instruction unInstr, string rawPointsTo, string unPointsTo -where - rawInstr = getOldInstruction(unInstr) and - not rawInstr instanceof Raw::VariableAddressInstruction and - ( - exists(Variable var, int rawBitOffset, int unBitOffset | - RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and - rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and - UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and - unPointsTo = var.toString() + getBitOffsetString(unBitOffset) - ) - or - exists(Variable var, int unBitOffset | - not RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), _) and - rawPointsTo = "none" and - UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and - unPointsTo = var.toString() + getBitOffsetString(unBitOffset) - ) - or - exists(Variable var, int rawBitOffset | - RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and - rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and - not UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), _) and - unPointsTo = "none" - ) - ) -select rawInstr.getLocation().toString(), rawInstr.getOperationString(), rawPointsTo, unPointsTo diff --git a/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql b/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql index 8025a455fc9..e97cea7670d 100644 --- a/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql +++ b/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql @@ -1,23 +1,25 @@ import default import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis +import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasConfiguration import semmle.code.cpp.ir.implementation.unaliased_ssa.IR import semmle.code.cpp.ir.implementation.UseSoundEscapeAnalysis -predicate shouldEscape(IRAutomaticUserVariable var) { - exists(string name | - name = var.getVariable().getName() and - name.matches("no_%") - ) +class InterestingAllocation extends VariableAllocation { + IRUserVariable userVar; + + InterestingAllocation() { userVar = this.getIRVariable() } + + final predicate shouldEscape() { userVar.getVariable().getName().matches("no_%") } } -from IRAutomaticUserVariable var +from InterestingAllocation var where exists(IRFunction irFunc | irFunc = var.getEnclosingIRFunction() and ( - shouldEscape(var) and variableAddressEscapes(var) + var.shouldEscape() and allocationEscapes(var) or - not shouldEscape(var) and not variableAddressEscapes(var) + not var.shouldEscape() and not allocationEscapes(var) ) ) select var diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp new file mode 100644 index 00000000000..249f08352ab --- /dev/null +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp @@ -0,0 +1,87 @@ +struct Point { + int x; + int y; +}; + +struct Base1 { + int b1; +}; + +struct Base2 { + int b2; +}; + +struct DerivedSI : Base1 { + int dsi; +}; + +struct DerivedMI : Base1, Base2 { + int dmi; +}; + +struct DerivedVI : virtual Base1 { + int dvi; +}; + +void Locals() { + Point pt = { //$ussa=pt + 1, //$ussa=pt[0..4) + 2 //$ussa=pt[4..8) + }; + int i = pt.x; //$ussa=pt[0..4) + i = pt.y; //$ussa=pt[4..8) + int* p = &pt.x; + i = *p; //$ussa=pt[0..4) + p = &pt.y; + i = *p; //$ussa=pt[4..8) +} + +void PointsTo( + int a, //$raw,ussa=a + Point& b, //$raw,ussa=b $ussa=*b + Point* c, //$raw,ussa=c $ussa=*c + int* d, //$raw,ussa=d $ussa=*d + DerivedSI* e, //$raw,ussa=e $ussa=*e + DerivedMI* f, //$raw,ussa=f $ussa=*f + DerivedVI* g //$raw,ussa=g $ussa=*g +) { + + int i = a; //$raw,ussa=a + i = *&a; //$raw,ussa=a + i = *(&a + 0); //$raw,ussa=a + i = b.x; //$raw,ussa=b $ussa=*b[0..4) + i = b.y; //$raw,ussa=b $ussa=*b[4..8) + i = c->x; //$raw,ussa=c $ussa=*c[0..4) + i = c->y; //$raw,ussa=c $ussa=*c[4..8) + i = *d; //$raw,ussa=d $ussa=*d[0..4) + i = *(d + 0); //$raw,ussa=d $ussa=*d[0..4) + i = d[5]; //$raw,ussa=d $ussa=*d[20..24) + i = 5[d]; //$raw,ussa=d $ussa=*d[20..24) + i = d[a]; //$raw,ussa=d $raw,ussa=a $ussa=*d[?..?) + i = a[d]; //$raw,ussa=d $raw,ussa=a $ussa=*d[?..?) + + int* p = &b.x; //$raw,ussa=b + i = *p; //$ussa=*b[0..4) + p = &b.y; //$raw,ussa=b + i = *p; //$ussa=*b[4..8) + p = &c->x; //$raw,ussa=c + i = *p; //$ussa=*c[0..4) + p = &c->y; //$raw,ussa=c + i = *p; //$ussa=*c[4..8) + p = &d[5]; //$raw,ussa=d + i = *p; //$ussa=*d[20..24) + p = &d[a]; //$raw,ussa=d $raw,ussa=a + i = *p; //$ussa=*d[?..?) + + Point* q = &c[a]; //$raw,ussa=c $raw,ussa=a + i = q->x; //$ussa=*c[?..?) + i = q->y; //$ussa=*c[?..?) + + i = e->b1; //$raw,ussa=e $ussa=*e[0..4) + i = e->dsi; //$raw,ussa=e $ussa=*e[4..8) + i = f->b1; //$raw,ussa=f $ussa=*f[0..4) + i = f->b2; //$raw,ussa=f $ussa=*f[4..8) + i = f->dmi; //$raw,ussa=f $ussa=*f[8..12) + i = g->b1; //$raw,ussa=g $ussa=*g[?..?) + i = g->dvi; //$raw,ussa=g $ussa=*g[8..12) +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.expected b/cpp/ql/test/library-tests/ir/points_to/points_to.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.ql b/cpp/ql/test/library-tests/ir/points_to/points_to.ql new file mode 100644 index 00000000000..89d1e31e119 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.ql @@ -0,0 +1,65 @@ +import cpp +private import TestUtilities.InlineExpectationsTest +private import semmle.code.cpp.ir.internal.IntegerConstant as Ints + +private predicate ignoreAllocation(string name) { + name = "i" or + name = "p" or + name = "q" +} + +module Raw { + private import semmle.code.cpp.ir.implementation.raw.IR + private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA + + private MemoryLocation getAMemoryAccess(Instruction instr) { + result = getResultMemoryLocation(instr) or + result = getOperandMemoryLocation(instr.getAnOperand()) + } + + class RawPointsToTest extends InlineExpectationsTest { + RawPointsToTest() { this = "RawPointsToTest" } + + override string getARelevantTag() { result = "raw" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(Instruction instr, MemoryLocation memLocation | + memLocation = getAMemoryAccess(instr) and + tag = "raw" and + not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and + value = memLocation.toString() and + element = instr.toString() and + location = instr.getLocation() + ) + } + } +} + +module UnaliasedSSA { + private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR + private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasedSSA + + private MemoryLocation getAMemoryAccess(Instruction instr) { + result = getResultMemoryLocation(instr) or + result = getOperandMemoryLocation(instr.getAnOperand()) + } + + class UnaliasedSSAPointsToTest extends InlineExpectationsTest { + UnaliasedSSAPointsToTest() { this = "UnaliasedSSAPointsToTest" } + + override string getARelevantTag() { result = "ussa" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(Instruction instr, MemoryLocation memLocation | + memLocation = getAMemoryAccess(instr) and + not memLocation instanceof AliasedVirtualVariable and + not memLocation instanceof AllNonLocalMemory and + tag = "ussa" and + not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and + value = memLocation.toString() and + element = instr.toString() and + location = instr.getLocation() + ) + } + } +} 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 413384da8fe..15c631f727c 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 @@ -8,13 +8,12 @@ ssa.cpp: # 13| m13_5(Point *) = InitializeParameter[p] : &:r13_4 # 13| r13_6(Point *) = Load : &:r13_4, m13_5 # 13| m13_7(unknown) = InitializeIndirection[p] : &:r13_6 -# 13| m13_8(unknown) = Chi : total:m13_2, partial:m13_7 -# 13| r13_9(glval) = VariableAddress[which1] : -# 13| m13_10(bool) = InitializeParameter[which1] : &:r13_9 -# 13| r13_11(glval) = VariableAddress[which2] : -# 13| m13_12(bool) = InitializeParameter[which2] : &:r13_11 +# 13| r13_8(glval) = VariableAddress[which1] : +# 13| m13_9(bool) = InitializeParameter[which1] : &:r13_8 +# 13| r13_10(glval) = VariableAddress[which2] : +# 13| m13_11(bool) = InitializeParameter[which2] : &:r13_10 # 14| r14_1(glval) = VariableAddress[which1] : -# 14| r14_2(bool) = Load : &:r14_1, m13_10 +# 14| r14_2(bool) = Load : &:r14_1, m13_9 # 14| v14_3(void) = ConditionalBranch : r14_2 #-----| False -> Block 2 #-----| True -> Block 1 @@ -23,29 +22,31 @@ ssa.cpp: # 15| r15_1(glval) = VariableAddress[p] : # 15| r15_2(Point *) = Load : &:r15_1, m13_5 # 15| r15_3(glval) = FieldAddress[x] : r15_2 -# 15| r15_4(int) = Load : &:r15_3, ~m13_8 +# 15| r15_4(int) = Load : &:r15_3, ~m13_7 # 15| r15_5(int) = Constant[1] : # 15| r15_6(int) = Add : r15_4, r15_5 # 15| m15_7(int) = Store : &:r15_3, r15_6 -# 15| m15_8(unknown) = Chi : total:m13_8, partial:m15_7 +# 15| m15_8(unknown) = Chi : total:m13_7, partial:m15_7 #-----| Goto -> Block 3 # 18| Block 2 # 18| r18_1(glval) = VariableAddress[p] : # 18| r18_2(Point *) = Load : &:r18_1, m13_5 # 18| r18_3(glval) = FieldAddress[y] : r18_2 -# 18| r18_4(int) = Load : &:r18_3, ~m13_8 +# 18| r18_4(int) = Load : &:r18_3, ~m13_7 # 18| r18_5(int) = Constant[1] : # 18| r18_6(int) = Add : r18_4, r18_5 # 18| m18_7(int) = Store : &:r18_3, r18_6 -# 18| m18_8(unknown) = Chi : total:m13_8, partial:m18_7 +# 18| m18_8(unknown) = Chi : total:m13_7, partial:m18_7 #-----| Goto -> Block 3 # 21| Block 3 -# 21| m21_1(unknown) = Phi : from 1:~m15_8, from 2:~m18_8 -# 21| r21_2(glval) = VariableAddress[which2] : -# 21| r21_3(bool) = Load : &:r21_2, m13_12 -# 21| v21_4(void) = ConditionalBranch : r21_3 +# 21| m21_1(int) = Phi : from 1:~m13_7, from 2:m18_7 +# 21| m21_2(int) = Phi : from 1:m15_7, from 2:~m13_7 +# 21| m21_3(unknown) = Phi : from 1:m15_8, from 2:m18_8 +# 21| r21_4(glval) = VariableAddress[which2] : +# 21| r21_5(bool) = Load : &:r21_4, m13_11 +# 21| v21_6(void) = ConditionalBranch : r21_5 #-----| False -> Block 5 #-----| True -> Block 4 @@ -53,43 +54,45 @@ ssa.cpp: # 22| r22_1(glval) = VariableAddress[p] : # 22| r22_2(Point *) = Load : &:r22_1, m13_5 # 22| r22_3(glval) = FieldAddress[x] : r22_2 -# 22| r22_4(int) = Load : &:r22_3, ~m21_1 +# 22| r22_4(int) = Load : &:r22_3, m21_2 # 22| r22_5(int) = Constant[1] : # 22| r22_6(int) = Add : r22_4, r22_5 # 22| m22_7(int) = Store : &:r22_3, r22_6 -# 22| m22_8(unknown) = Chi : total:m21_1, partial:m22_7 +# 22| m22_8(unknown) = Chi : total:m21_3, partial:m22_7 #-----| Goto -> Block 6 # 25| Block 5 # 25| r25_1(glval) = VariableAddress[p] : # 25| r25_2(Point *) = Load : &:r25_1, m13_5 # 25| r25_3(glval) = FieldAddress[y] : r25_2 -# 25| r25_4(int) = Load : &:r25_3, ~m21_1 +# 25| r25_4(int) = Load : &:r25_3, m21_1 # 25| r25_5(int) = Constant[1] : # 25| r25_6(int) = Add : r25_4, r25_5 # 25| m25_7(int) = Store : &:r25_3, r25_6 -# 25| m25_8(unknown) = Chi : total:m21_1, partial:m25_7 +# 25| m25_8(unknown) = Chi : total:m21_3, partial:m25_7 #-----| Goto -> Block 6 # 28| Block 6 -# 28| m28_1(unknown) = Phi : from 4:~m22_8, from 5:~m25_8 -# 28| r28_2(glval) = VariableAddress[#return] : -# 28| r28_3(glval) = VariableAddress[p] : -# 28| r28_4(Point *) = Load : &:r28_3, m13_5 -# 28| r28_5(glval) = FieldAddress[x] : r28_4 -# 28| r28_6(int) = Load : &:r28_5, ~m28_1 -# 28| r28_7(glval) = VariableAddress[p] : -# 28| r28_8(Point *) = Load : &:r28_7, m13_5 -# 28| r28_9(glval) = FieldAddress[y] : r28_8 -# 28| r28_10(int) = Load : &:r28_9, ~m28_1 -# 28| r28_11(int) = Add : r28_6, r28_10 -# 28| m28_12(int) = Store : &:r28_2, r28_11 -# 13| v13_13(void) = ReturnIndirection : &:r13_6, ~m28_1 -# 13| r13_14(glval) = VariableAddress[#return] : -# 13| v13_15(void) = ReturnValue : &:r13_14, m28_12 -# 13| v13_16(void) = UnmodeledUse : mu* -# 13| v13_17(void) = AliasedUse : ~m28_1 -# 13| v13_18(void) = ExitFunction : +# 28| m28_1(int) = Phi : from 4:m21_1, from 5:m25_7 +# 28| m28_2(int) = Phi : from 4:m22_7, from 5:m21_2 +# 28| m28_3(unknown) = Phi : from 4:m22_8, from 5:m25_8 +# 28| r28_4(glval) = VariableAddress[#return] : +# 28| r28_5(glval) = VariableAddress[p] : +# 28| r28_6(Point *) = Load : &:r28_5, m13_5 +# 28| r28_7(glval) = FieldAddress[x] : r28_6 +# 28| r28_8(int) = Load : &:r28_7, m28_2 +# 28| r28_9(glval) = VariableAddress[p] : +# 28| r28_10(Point *) = Load : &:r28_9, m13_5 +# 28| r28_11(glval) = FieldAddress[y] : r28_10 +# 28| r28_12(int) = Load : &:r28_11, m28_1 +# 28| r28_13(int) = Add : r28_8, r28_12 +# 28| m28_14(int) = Store : &:r28_4, r28_13 +# 13| v13_12(void) = ReturnIndirection : &:r13_6, m28_3 +# 13| r13_13(glval) = VariableAddress[#return] : +# 13| v13_14(void) = ReturnValue : &:r13_13, m28_14 +# 13| v13_15(void) = UnmodeledUse : mu* +# 13| v13_16(void) = AliasedUse : ~m13_2 +# 13| v13_17(void) = ExitFunction : # 31| int UnreachableViaGoto() # 31| Block 0 @@ -212,7 +215,6 @@ ssa.cpp: # 68| m68_7(char *) = InitializeParameter[p] : &:r68_6 # 68| r68_8(char *) = Load : &:r68_6, m68_7 # 68| m68_9(unknown) = InitializeIndirection[p] : &:r68_8 -# 68| m68_10(unknown) = Chi : total:m68_2, partial:m68_9 #-----| Goto -> Block 3 # 70| Block 1 @@ -229,16 +231,16 @@ ssa.cpp: # 71| Block 2 # 71| v71_1(void) = NoOp : -# 68| v68_11(void) = ReturnIndirection : &:r68_8, ~m69_3 -# 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = UnmodeledUse : mu* -# 68| v68_14(void) = AliasedUse : ~m69_3 -# 68| v68_15(void) = ExitFunction : +# 68| v68_10(void) = ReturnIndirection : &:r68_8, m68_9 +# 68| v68_11(void) = ReturnVoid : +# 68| v68_12(void) = UnmodeledUse : mu* +# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_14(void) = ExitFunction : # 69| Block 3 # 69| m69_1(char *) = Phi : from 0:m68_7, from 1:m70_6 # 69| m69_2(int) = Phi : from 0:m68_5, from 1:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_10, from 1:~m70_9 +# 69| m69_3(unknown) = Phi : from 0:~m68_2, from 1:~m70_9 # 69| r69_4(glval) = VariableAddress[n] : # 69| r69_5(int) = Load : &:r69_4, m69_2 # 69| r69_6(int) = Constant[1] : @@ -759,20 +761,19 @@ ssa.cpp: # 179| m179_5(int *) = InitializeParameter[p] : &:r179_4 # 179| r179_6(int *) = Load : &:r179_4, m179_5 # 179| m179_7(unknown) = InitializeIndirection[p] : &:r179_6 -# 179| m179_8(unknown) = Chi : total:m179_2, partial:m179_7 -# 180| m180_1(unknown) = InlineAsm : ~m179_8 -# 180| m180_2(unknown) = Chi : total:m179_8, partial:m180_1 +# 180| m180_1(unknown) = InlineAsm : ~m179_2 +# 180| m180_2(unknown) = Chi : total:m179_2, partial:m180_1 # 181| r181_1(glval) = VariableAddress[#return] : # 181| r181_2(glval) = VariableAddress[p] : # 181| r181_3(int *) = Load : &:r181_2, m179_5 -# 181| r181_4(int) = Load : &:r181_3, ~m180_2 +# 181| r181_4(int) = Load : &:r181_3, ~m179_7 # 181| m181_5(int) = Store : &:r181_1, r181_4 -# 179| v179_9(void) = ReturnIndirection : &:r179_6, ~m180_2 -# 179| r179_10(glval) = VariableAddress[#return] : -# 179| v179_11(void) = ReturnValue : &:r179_10, m181_5 -# 179| v179_12(void) = UnmodeledUse : mu* -# 179| v179_13(void) = AliasedUse : ~m180_2 -# 179| v179_14(void) = ExitFunction : +# 179| v179_8(void) = ReturnIndirection : &:r179_6, m179_7 +# 179| r179_9(glval) = VariableAddress[#return] : +# 179| v179_10(void) = ReturnValue : &:r179_9, m181_5 +# 179| v179_11(void) = UnmodeledUse : mu* +# 179| v179_12(void) = AliasedUse : ~m180_2 +# 179| v179_13(void) = ExitFunction : # 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) # 184| Block 0 @@ -783,45 +784,41 @@ ssa.cpp: # 184| m184_5(unsigned int &) = InitializeParameter[a] : &:r184_4 # 184| r184_6(unsigned int &) = Load : &:r184_4, m184_5 # 184| m184_7(unknown) = InitializeIndirection[a] : &:r184_6 -# 184| m184_8(unknown) = Chi : total:m184_2, partial:m184_7 -# 184| r184_9(glval) = VariableAddress[b] : -# 184| m184_10(unsigned int &) = InitializeParameter[b] : &:r184_9 -# 184| r184_11(unsigned int &) = Load : &:r184_9, m184_10 -# 184| m184_12(unknown) = InitializeIndirection[b] : &:r184_11 -# 184| m184_13(unknown) = Chi : total:m184_8, partial:m184_12 -# 184| r184_14(glval) = VariableAddress[c] : -# 184| m184_15(unsigned int &) = InitializeParameter[c] : &:r184_14 -# 184| r184_16(unsigned int &) = Load : &:r184_14, m184_15 -# 184| m184_17(unknown) = InitializeIndirection[c] : &:r184_16 -# 184| m184_18(unknown) = Chi : total:m184_13, partial:m184_17 -# 184| r184_19(glval) = VariableAddress[d] : -# 184| m184_20(unsigned int &) = InitializeParameter[d] : &:r184_19 -# 184| r184_21(unsigned int &) = Load : &:r184_19, m184_20 -# 184| m184_22(unknown) = InitializeIndirection[d] : &:r184_21 -# 184| m184_23(unknown) = Chi : total:m184_18, partial:m184_22 +# 184| r184_8(glval) = VariableAddress[b] : +# 184| m184_9(unsigned int &) = InitializeParameter[b] : &:r184_8 +# 184| r184_10(unsigned int &) = Load : &:r184_8, m184_9 +# 184| m184_11(unknown) = InitializeIndirection[b] : &:r184_10 +# 184| r184_12(glval) = VariableAddress[c] : +# 184| m184_13(unsigned int &) = InitializeParameter[c] : &:r184_12 +# 184| r184_14(unsigned int &) = Load : &:r184_12, m184_13 +# 184| m184_15(unknown) = InitializeIndirection[c] : &:r184_14 +# 184| r184_16(glval) = VariableAddress[d] : +# 184| m184_17(unsigned int &) = InitializeParameter[d] : &:r184_16 +# 184| r184_18(unsigned int &) = Load : &:r184_16, m184_17 +# 184| m184_19(unknown) = InitializeIndirection[d] : &:r184_18 # 189| r189_1(glval) = VariableAddress[a] : # 189| r189_2(unsigned int &) = Load : &:r189_1, m184_5 # 189| r189_3(glval) = CopyValue : r189_2 # 189| r189_4(glval) = VariableAddress[b] : -# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_10 +# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_9 # 189| r189_6(glval) = CopyValue : r189_5 # 190| r190_1(glval) = VariableAddress[c] : -# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_15 -# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_23 +# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_13 +# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_15 # 190| r190_4(glval) = VariableAddress[d] : -# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_20 -# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23 -# 186| m186_1(unknown) = InlineAsm : ~m184_23, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 -# 186| m186_2(unknown) = Chi : total:m184_23, partial:m186_1 +# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_17 +# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_19 +# 186| m186_1(unknown) = InlineAsm : ~m184_11, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 +# 186| m186_2(unknown) = Chi : total:m184_11, partial:m186_1 # 192| v192_1(void) = NoOp : -# 184| v184_24(void) = ReturnIndirection : &:r184_6, ~m186_2 -# 184| v184_25(void) = ReturnIndirection : &:r184_11, ~m186_2 -# 184| v184_26(void) = ReturnIndirection : &:r184_16, ~m186_2 -# 184| v184_27(void) = ReturnIndirection : &:r184_21, ~m186_2 -# 184| v184_28(void) = ReturnVoid : -# 184| v184_29(void) = UnmodeledUse : mu* -# 184| v184_30(void) = AliasedUse : ~m186_2 -# 184| v184_31(void) = ExitFunction : +# 184| v184_20(void) = ReturnIndirection : &:r184_6, ~m186_2 +# 184| v184_21(void) = ReturnIndirection : &:r184_10, ~m186_2 +# 184| v184_22(void) = ReturnIndirection : &:r184_14, m184_15 +# 184| v184_23(void) = ReturnIndirection : &:r184_18, m184_19 +# 184| v184_24(void) = ReturnVoid : +# 184| v184_25(void) = UnmodeledUse : mu* +# 184| v184_26(void) = AliasedUse : ~m186_2 +# 184| v184_27(void) = ExitFunction : # 198| int PureFunctions(char*, char*, int) # 198| Block 0 @@ -832,41 +829,39 @@ ssa.cpp: # 198| m198_5(char *) = InitializeParameter[str1] : &:r198_4 # 198| r198_6(char *) = Load : &:r198_4, m198_5 # 198| m198_7(unknown) = InitializeIndirection[str1] : &:r198_6 -# 198| m198_8(unknown) = Chi : total:m198_2, partial:m198_7 -# 198| r198_9(glval) = VariableAddress[str2] : -# 198| m198_10(char *) = InitializeParameter[str2] : &:r198_9 -# 198| r198_11(char *) = Load : &:r198_9, m198_10 -# 198| m198_12(unknown) = InitializeIndirection[str2] : &:r198_11 -# 198| m198_13(unknown) = Chi : total:m198_8, partial:m198_12 -# 198| r198_14(glval) = VariableAddress[x] : -# 198| m198_15(int) = InitializeParameter[x] : &:r198_14 +# 198| r198_8(glval) = VariableAddress[str2] : +# 198| m198_9(char *) = InitializeParameter[str2] : &:r198_8 +# 198| r198_10(char *) = Load : &:r198_8, m198_9 +# 198| m198_11(unknown) = InitializeIndirection[str2] : &:r198_10 +# 198| r198_12(glval) = VariableAddress[x] : +# 198| m198_13(int) = InitializeParameter[x] : &:r198_12 # 199| r199_1(glval) = VariableAddress[ret] : # 199| r199_2(glval) = FunctionAddress[strcmp] : # 199| r199_3(glval) = VariableAddress[str1] : # 199| r199_4(char *) = Load : &:r199_3, m198_5 # 199| r199_5(char *) = Convert : r199_4 # 199| r199_6(glval) = VariableAddress[str2] : -# 199| r199_7(char *) = Load : &:r199_6, m198_10 +# 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 # 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 -# 199| v199_10(void) = ^CallReadSideEffect : ~m198_13 -# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_13 -# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13 +# 199| v199_10(void) = ^CallReadSideEffect : ~m198_2 +# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_7 +# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_11 # 199| m199_13(int) = Store : &:r199_1, r199_9 # 200| r200_1(glval) = FunctionAddress[strlen] : # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 # 200| r200_5(int) = Call : func:r200_1, 0:r200_4 -# 200| v200_6(void) = ^CallReadSideEffect : ~m198_13 -# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_13 +# 200| v200_6(void) = ^CallReadSideEffect : ~m198_2 +# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_7 # 200| r200_8(glval) = VariableAddress[ret] : # 200| r200_9(int) = Load : &:r200_8, m199_13 # 200| r200_10(int) = Add : r200_9, r200_5 # 200| m200_11(int) = Store : &:r200_8, r200_10 # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : -# 201| r201_3(int) = Load : &:r201_2, m198_15 +# 201| r201_3(int) = Load : &:r201_2, m198_13 # 201| r201_4(int) = Call : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_11 @@ -876,13 +871,13 @@ ssa.cpp: # 202| r202_2(glval) = VariableAddress[ret] : # 202| r202_3(int) = Load : &:r202_2, m201_8 # 202| m202_4(int) = Store : &:r202_1, r202_3 -# 198| v198_16(void) = ReturnIndirection : &:r198_6, ~m198_13 -# 198| v198_17(void) = ReturnIndirection : &:r198_11, ~m198_13 -# 198| r198_18(glval) = VariableAddress[#return] : -# 198| v198_19(void) = ReturnValue : &:r198_18, m202_4 -# 198| v198_20(void) = UnmodeledUse : mu* -# 198| v198_21(void) = AliasedUse : ~m198_13 -# 198| v198_22(void) = ExitFunction : +# 198| v198_14(void) = ReturnIndirection : &:r198_6, m198_7 +# 198| v198_15(void) = ReturnIndirection : &:r198_10, m198_11 +# 198| r198_16(glval) = VariableAddress[#return] : +# 198| v198_17(void) = ReturnValue : &:r198_16, m202_4 +# 198| v198_18(void) = UnmodeledUse : mu* +# 198| v198_19(void) = AliasedUse : ~m198_2 +# 198| v198_20(void) = ExitFunction : # 207| int ModeledCallTarget(int) # 207| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index b1a174f2b0b..ef8570bd1a5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -8,13 +8,12 @@ ssa.cpp: # 13| m13_5(Point *) = InitializeParameter[p] : &:r13_4 # 13| r13_6(Point *) = Load : &:r13_4, m13_5 # 13| m13_7(unknown) = InitializeIndirection[p] : &:r13_6 -# 13| m13_8(unknown) = Chi : total:m13_2, partial:m13_7 -# 13| r13_9(glval) = VariableAddress[which1] : -# 13| m13_10(bool) = InitializeParameter[which1] : &:r13_9 -# 13| r13_11(glval) = VariableAddress[which2] : -# 13| m13_12(bool) = InitializeParameter[which2] : &:r13_11 +# 13| r13_8(glval) = VariableAddress[which1] : +# 13| m13_9(bool) = InitializeParameter[which1] : &:r13_8 +# 13| r13_10(glval) = VariableAddress[which2] : +# 13| m13_11(bool) = InitializeParameter[which2] : &:r13_10 # 14| r14_1(glval) = VariableAddress[which1] : -# 14| r14_2(bool) = Load : &:r14_1, m13_10 +# 14| r14_2(bool) = Load : &:r14_1, m13_9 # 14| v14_3(void) = ConditionalBranch : r14_2 #-----| False -> Block 2 #-----| True -> Block 1 @@ -23,29 +22,31 @@ ssa.cpp: # 15| r15_1(glval) = VariableAddress[p] : # 15| r15_2(Point *) = Load : &:r15_1, m13_5 # 15| r15_3(glval) = FieldAddress[x] : r15_2 -# 15| r15_4(int) = Load : &:r15_3, ~m13_8 +# 15| r15_4(int) = Load : &:r15_3, ~m13_7 # 15| r15_5(int) = Constant[1] : # 15| r15_6(int) = Add : r15_4, r15_5 # 15| m15_7(int) = Store : &:r15_3, r15_6 -# 15| m15_8(unknown) = Chi : total:m13_8, partial:m15_7 +# 15| m15_8(unknown) = Chi : total:m13_7, partial:m15_7 #-----| Goto -> Block 3 # 18| Block 2 # 18| r18_1(glval) = VariableAddress[p] : # 18| r18_2(Point *) = Load : &:r18_1, m13_5 # 18| r18_3(glval) = FieldAddress[y] : r18_2 -# 18| r18_4(int) = Load : &:r18_3, ~m13_8 +# 18| r18_4(int) = Load : &:r18_3, ~m13_7 # 18| r18_5(int) = Constant[1] : # 18| r18_6(int) = Add : r18_4, r18_5 # 18| m18_7(int) = Store : &:r18_3, r18_6 -# 18| m18_8(unknown) = Chi : total:m13_8, partial:m18_7 +# 18| m18_8(unknown) = Chi : total:m13_7, partial:m18_7 #-----| Goto -> Block 3 # 21| Block 3 -# 21| m21_1(unknown) = Phi : from 1:~m15_8, from 2:~m18_8 -# 21| r21_2(glval) = VariableAddress[which2] : -# 21| r21_3(bool) = Load : &:r21_2, m13_12 -# 21| v21_4(void) = ConditionalBranch : r21_3 +# 21| m21_1(int) = Phi : from 1:~m13_7, from 2:m18_7 +# 21| m21_2(int) = Phi : from 1:m15_7, from 2:~m13_7 +# 21| m21_3(unknown) = Phi : from 1:m15_8, from 2:m18_8 +# 21| r21_4(glval) = VariableAddress[which2] : +# 21| r21_5(bool) = Load : &:r21_4, m13_11 +# 21| v21_6(void) = ConditionalBranch : r21_5 #-----| False -> Block 5 #-----| True -> Block 4 @@ -53,43 +54,45 @@ ssa.cpp: # 22| r22_1(glval) = VariableAddress[p] : # 22| r22_2(Point *) = Load : &:r22_1, m13_5 # 22| r22_3(glval) = FieldAddress[x] : r22_2 -# 22| r22_4(int) = Load : &:r22_3, ~m21_1 +# 22| r22_4(int) = Load : &:r22_3, m21_2 # 22| r22_5(int) = Constant[1] : # 22| r22_6(int) = Add : r22_4, r22_5 # 22| m22_7(int) = Store : &:r22_3, r22_6 -# 22| m22_8(unknown) = Chi : total:m21_1, partial:m22_7 +# 22| m22_8(unknown) = Chi : total:m21_3, partial:m22_7 #-----| Goto -> Block 6 # 25| Block 5 # 25| r25_1(glval) = VariableAddress[p] : # 25| r25_2(Point *) = Load : &:r25_1, m13_5 # 25| r25_3(glval) = FieldAddress[y] : r25_2 -# 25| r25_4(int) = Load : &:r25_3, ~m21_1 +# 25| r25_4(int) = Load : &:r25_3, m21_1 # 25| r25_5(int) = Constant[1] : # 25| r25_6(int) = Add : r25_4, r25_5 # 25| m25_7(int) = Store : &:r25_3, r25_6 -# 25| m25_8(unknown) = Chi : total:m21_1, partial:m25_7 +# 25| m25_8(unknown) = Chi : total:m21_3, partial:m25_7 #-----| Goto -> Block 6 # 28| Block 6 -# 28| m28_1(unknown) = Phi : from 4:~m22_8, from 5:~m25_8 -# 28| r28_2(glval) = VariableAddress[#return] : -# 28| r28_3(glval) = VariableAddress[p] : -# 28| r28_4(Point *) = Load : &:r28_3, m13_5 -# 28| r28_5(glval) = FieldAddress[x] : r28_4 -# 28| r28_6(int) = Load : &:r28_5, ~m28_1 -# 28| r28_7(glval) = VariableAddress[p] : -# 28| r28_8(Point *) = Load : &:r28_7, m13_5 -# 28| r28_9(glval) = FieldAddress[y] : r28_8 -# 28| r28_10(int) = Load : &:r28_9, ~m28_1 -# 28| r28_11(int) = Add : r28_6, r28_10 -# 28| m28_12(int) = Store : &:r28_2, r28_11 -# 13| v13_13(void) = ReturnIndirection : &:r13_6, ~m28_1 -# 13| r13_14(glval) = VariableAddress[#return] : -# 13| v13_15(void) = ReturnValue : &:r13_14, m28_12 -# 13| v13_16(void) = UnmodeledUse : mu* -# 13| v13_17(void) = AliasedUse : ~m28_1 -# 13| v13_18(void) = ExitFunction : +# 28| m28_1(int) = Phi : from 4:m21_1, from 5:m25_7 +# 28| m28_2(int) = Phi : from 4:m22_7, from 5:m21_2 +# 28| m28_3(unknown) = Phi : from 4:m22_8, from 5:m25_8 +# 28| r28_4(glval) = VariableAddress[#return] : +# 28| r28_5(glval) = VariableAddress[p] : +# 28| r28_6(Point *) = Load : &:r28_5, m13_5 +# 28| r28_7(glval) = FieldAddress[x] : r28_6 +# 28| r28_8(int) = Load : &:r28_7, m28_2 +# 28| r28_9(glval) = VariableAddress[p] : +# 28| r28_10(Point *) = Load : &:r28_9, m13_5 +# 28| r28_11(glval) = FieldAddress[y] : r28_10 +# 28| r28_12(int) = Load : &:r28_11, m28_1 +# 28| r28_13(int) = Add : r28_8, r28_12 +# 28| m28_14(int) = Store : &:r28_4, r28_13 +# 13| v13_12(void) = ReturnIndirection : &:r13_6, m28_3 +# 13| r13_13(glval) = VariableAddress[#return] : +# 13| v13_14(void) = ReturnValue : &:r13_13, m28_14 +# 13| v13_15(void) = UnmodeledUse : mu* +# 13| v13_16(void) = AliasedUse : ~m13_2 +# 13| v13_17(void) = ExitFunction : # 31| int UnreachableViaGoto() # 31| Block 0 @@ -212,7 +215,6 @@ ssa.cpp: # 68| m68_7(char *) = InitializeParameter[p] : &:r68_6 # 68| r68_8(char *) = Load : &:r68_6, m68_7 # 68| m68_9(unknown) = InitializeIndirection[p] : &:r68_8 -# 68| m68_10(unknown) = Chi : total:m68_2, partial:m68_9 #-----| Goto -> Block 3 # 70| Block 1 @@ -229,16 +231,16 @@ ssa.cpp: # 71| Block 2 # 71| v71_1(void) = NoOp : -# 68| v68_11(void) = ReturnIndirection : &:r68_8, ~m69_3 -# 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = UnmodeledUse : mu* -# 68| v68_14(void) = AliasedUse : ~m69_3 -# 68| v68_15(void) = ExitFunction : +# 68| v68_10(void) = ReturnIndirection : &:r68_8, m68_9 +# 68| v68_11(void) = ReturnVoid : +# 68| v68_12(void) = UnmodeledUse : mu* +# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_14(void) = ExitFunction : # 69| Block 3 # 69| m69_1(char *) = Phi : from 0:m68_7, from 1:m70_6 # 69| m69_2(int) = Phi : from 0:m68_5, from 1:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_10, from 1:~m70_9 +# 69| m69_3(unknown) = Phi : from 0:~m68_2, from 1:~m70_9 # 69| r69_4(glval) = VariableAddress[n] : # 69| r69_5(int) = Load : &:r69_4, m69_2 # 69| r69_6(int) = Constant[1] : @@ -756,20 +758,19 @@ ssa.cpp: # 179| m179_5(int *) = InitializeParameter[p] : &:r179_4 # 179| r179_6(int *) = Load : &:r179_4, m179_5 # 179| m179_7(unknown) = InitializeIndirection[p] : &:r179_6 -# 179| m179_8(unknown) = Chi : total:m179_2, partial:m179_7 -# 180| m180_1(unknown) = InlineAsm : ~m179_8 -# 180| m180_2(unknown) = Chi : total:m179_8, partial:m180_1 +# 180| m180_1(unknown) = InlineAsm : ~m179_2 +# 180| m180_2(unknown) = Chi : total:m179_2, partial:m180_1 # 181| r181_1(glval) = VariableAddress[#return] : # 181| r181_2(glval) = VariableAddress[p] : # 181| r181_3(int *) = Load : &:r181_2, m179_5 -# 181| r181_4(int) = Load : &:r181_3, ~m180_2 +# 181| r181_4(int) = Load : &:r181_3, ~m179_7 # 181| m181_5(int) = Store : &:r181_1, r181_4 -# 179| v179_9(void) = ReturnIndirection : &:r179_6, ~m180_2 -# 179| r179_10(glval) = VariableAddress[#return] : -# 179| v179_11(void) = ReturnValue : &:r179_10, m181_5 -# 179| v179_12(void) = UnmodeledUse : mu* -# 179| v179_13(void) = AliasedUse : ~m180_2 -# 179| v179_14(void) = ExitFunction : +# 179| v179_8(void) = ReturnIndirection : &:r179_6, m179_7 +# 179| r179_9(glval) = VariableAddress[#return] : +# 179| v179_10(void) = ReturnValue : &:r179_9, m181_5 +# 179| v179_11(void) = UnmodeledUse : mu* +# 179| v179_12(void) = AliasedUse : ~m180_2 +# 179| v179_13(void) = ExitFunction : # 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) # 184| Block 0 @@ -780,45 +781,41 @@ ssa.cpp: # 184| m184_5(unsigned int &) = InitializeParameter[a] : &:r184_4 # 184| r184_6(unsigned int &) = Load : &:r184_4, m184_5 # 184| m184_7(unknown) = InitializeIndirection[a] : &:r184_6 -# 184| m184_8(unknown) = Chi : total:m184_2, partial:m184_7 -# 184| r184_9(glval) = VariableAddress[b] : -# 184| m184_10(unsigned int &) = InitializeParameter[b] : &:r184_9 -# 184| r184_11(unsigned int &) = Load : &:r184_9, m184_10 -# 184| m184_12(unknown) = InitializeIndirection[b] : &:r184_11 -# 184| m184_13(unknown) = Chi : total:m184_8, partial:m184_12 -# 184| r184_14(glval) = VariableAddress[c] : -# 184| m184_15(unsigned int &) = InitializeParameter[c] : &:r184_14 -# 184| r184_16(unsigned int &) = Load : &:r184_14, m184_15 -# 184| m184_17(unknown) = InitializeIndirection[c] : &:r184_16 -# 184| m184_18(unknown) = Chi : total:m184_13, partial:m184_17 -# 184| r184_19(glval) = VariableAddress[d] : -# 184| m184_20(unsigned int &) = InitializeParameter[d] : &:r184_19 -# 184| r184_21(unsigned int &) = Load : &:r184_19, m184_20 -# 184| m184_22(unknown) = InitializeIndirection[d] : &:r184_21 -# 184| m184_23(unknown) = Chi : total:m184_18, partial:m184_22 +# 184| r184_8(glval) = VariableAddress[b] : +# 184| m184_9(unsigned int &) = InitializeParameter[b] : &:r184_8 +# 184| r184_10(unsigned int &) = Load : &:r184_8, m184_9 +# 184| m184_11(unknown) = InitializeIndirection[b] : &:r184_10 +# 184| r184_12(glval) = VariableAddress[c] : +# 184| m184_13(unsigned int &) = InitializeParameter[c] : &:r184_12 +# 184| r184_14(unsigned int &) = Load : &:r184_12, m184_13 +# 184| m184_15(unknown) = InitializeIndirection[c] : &:r184_14 +# 184| r184_16(glval) = VariableAddress[d] : +# 184| m184_17(unsigned int &) = InitializeParameter[d] : &:r184_16 +# 184| r184_18(unsigned int &) = Load : &:r184_16, m184_17 +# 184| m184_19(unknown) = InitializeIndirection[d] : &:r184_18 # 189| r189_1(glval) = VariableAddress[a] : # 189| r189_2(unsigned int &) = Load : &:r189_1, m184_5 # 189| r189_3(glval) = CopyValue : r189_2 # 189| r189_4(glval) = VariableAddress[b] : -# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_10 +# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_9 # 189| r189_6(glval) = CopyValue : r189_5 # 190| r190_1(glval) = VariableAddress[c] : -# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_15 -# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_23 +# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_13 +# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_15 # 190| r190_4(glval) = VariableAddress[d] : -# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_20 -# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23 -# 186| m186_1(unknown) = InlineAsm : ~m184_23, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 -# 186| m186_2(unknown) = Chi : total:m184_23, partial:m186_1 +# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_17 +# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_19 +# 186| m186_1(unknown) = InlineAsm : ~m184_2, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 +# 186| m186_2(unknown) = Chi : total:m184_2, partial:m186_1 # 192| v192_1(void) = NoOp : -# 184| v184_24(void) = ReturnIndirection : &:r184_6, ~m186_2 -# 184| v184_25(void) = ReturnIndirection : &:r184_11, ~m186_2 -# 184| v184_26(void) = ReturnIndirection : &:r184_16, ~m186_2 -# 184| v184_27(void) = ReturnIndirection : &:r184_21, ~m186_2 -# 184| v184_28(void) = ReturnVoid : -# 184| v184_29(void) = UnmodeledUse : mu* -# 184| v184_30(void) = AliasedUse : ~m186_2 -# 184| v184_31(void) = ExitFunction : +# 184| v184_20(void) = ReturnIndirection : &:r184_6, m184_7 +# 184| v184_21(void) = ReturnIndirection : &:r184_10, m184_11 +# 184| v184_22(void) = ReturnIndirection : &:r184_14, m184_15 +# 184| v184_23(void) = ReturnIndirection : &:r184_18, m184_19 +# 184| v184_24(void) = ReturnVoid : +# 184| v184_25(void) = UnmodeledUse : mu* +# 184| v184_26(void) = AliasedUse : ~m186_2 +# 184| v184_27(void) = ExitFunction : # 198| int PureFunctions(char*, char*, int) # 198| Block 0 @@ -829,41 +826,39 @@ ssa.cpp: # 198| m198_5(char *) = InitializeParameter[str1] : &:r198_4 # 198| r198_6(char *) = Load : &:r198_4, m198_5 # 198| m198_7(unknown) = InitializeIndirection[str1] : &:r198_6 -# 198| m198_8(unknown) = Chi : total:m198_2, partial:m198_7 -# 198| r198_9(glval) = VariableAddress[str2] : -# 198| m198_10(char *) = InitializeParameter[str2] : &:r198_9 -# 198| r198_11(char *) = Load : &:r198_9, m198_10 -# 198| m198_12(unknown) = InitializeIndirection[str2] : &:r198_11 -# 198| m198_13(unknown) = Chi : total:m198_8, partial:m198_12 -# 198| r198_14(glval) = VariableAddress[x] : -# 198| m198_15(int) = InitializeParameter[x] : &:r198_14 +# 198| r198_8(glval) = VariableAddress[str2] : +# 198| m198_9(char *) = InitializeParameter[str2] : &:r198_8 +# 198| r198_10(char *) = Load : &:r198_8, m198_9 +# 198| m198_11(unknown) = InitializeIndirection[str2] : &:r198_10 +# 198| r198_12(glval) = VariableAddress[x] : +# 198| m198_13(int) = InitializeParameter[x] : &:r198_12 # 199| r199_1(glval) = VariableAddress[ret] : # 199| r199_2(glval) = FunctionAddress[strcmp] : # 199| r199_3(glval) = VariableAddress[str1] : # 199| r199_4(char *) = Load : &:r199_3, m198_5 # 199| r199_5(char *) = Convert : r199_4 # 199| r199_6(glval) = VariableAddress[str2] : -# 199| r199_7(char *) = Load : &:r199_6, m198_10 +# 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 # 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 -# 199| v199_10(void) = ^CallReadSideEffect : ~m198_13 -# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_13 -# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13 +# 199| v199_10(void) = ^CallReadSideEffect : ~m198_2 +# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_7 +# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_11 # 199| m199_13(int) = Store : &:r199_1, r199_9 # 200| r200_1(glval) = FunctionAddress[strlen] : # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 # 200| r200_5(int) = Call : func:r200_1, 0:r200_4 -# 200| v200_6(void) = ^CallReadSideEffect : ~m198_13 -# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_13 +# 200| v200_6(void) = ^CallReadSideEffect : ~m198_2 +# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_7 # 200| r200_8(glval) = VariableAddress[ret] : # 200| r200_9(int) = Load : &:r200_8, m199_13 # 200| r200_10(int) = Add : r200_9, r200_5 # 200| m200_11(int) = Store : &:r200_8, r200_10 # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : -# 201| r201_3(int) = Load : &:r201_2, m198_15 +# 201| r201_3(int) = Load : &:r201_2, m198_13 # 201| r201_4(int) = Call : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_11 @@ -873,13 +868,13 @@ ssa.cpp: # 202| r202_2(glval) = VariableAddress[ret] : # 202| r202_3(int) = Load : &:r202_2, m201_8 # 202| m202_4(int) = Store : &:r202_1, r202_3 -# 198| v198_16(void) = ReturnIndirection : &:r198_6, ~m198_13 -# 198| v198_17(void) = ReturnIndirection : &:r198_11, ~m198_13 -# 198| r198_18(glval) = VariableAddress[#return] : -# 198| v198_19(void) = ReturnValue : &:r198_18, m202_4 -# 198| v198_20(void) = UnmodeledUse : mu* -# 198| v198_21(void) = AliasedUse : ~m198_13 -# 198| v198_22(void) = ExitFunction : +# 198| v198_14(void) = ReturnIndirection : &:r198_6, m198_7 +# 198| v198_15(void) = ReturnIndirection : &:r198_10, m198_11 +# 198| r198_16(glval) = VariableAddress[#return] : +# 198| v198_17(void) = ReturnValue : &:r198_16, m202_4 +# 198| v198_18(void) = UnmodeledUse : mu* +# 198| v198_19(void) = AliasedUse : ~m198_2 +# 198| v198_20(void) = ExitFunction : # 207| int ModeledCallTarget(int) # 207| Block 0 diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected index ebd22e357c2..c0671e967cc 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected @@ -35,14 +35,14 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| VacuousDestructorCall.cpp:2:29:2:29 | Chi: y | +| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | | condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt | | condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt | | condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt | | condition_decls.cpp:48:52:48:53 | Chi: call to BoxedInt | | cpp17.cpp:15:11:15:21 | Convert: (void *)... | | misc.c:171:10:171:13 | Uninitialized: definition of str2 | -| misc.c:219:47:219:48 | Chi: sp | +| misc.c:219:47:219:48 | InitializeIndirection: sp | | ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | | ms_try_mix.cpp:11:12:11:15 | Chi: call to C | | ms_try_mix.cpp:28:12:28:15 | Chi: call to C | diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 38386b9c2fd..d8cda203eff 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -266,8 +266,6 @@ test.cpp: # 39| valnum = m39_9 # 39| m39_11(unknown) = InitializeIndirection[p2] : &:r39_10 # 39| valnum = unique -# 39| m39_12(unknown) = Chi : total:m39_2, partial:m39_11 -# 39| valnum = unique # 40| r40_1(glval) = VariableAddress[x] : # 40| valnum = r40_1 # 40| m40_2(int) = Uninitialized[x] : &:r40_1 @@ -292,7 +290,7 @@ test.cpp: # 43| valnum = r43_5 # 43| r43_6(glval) = VariableAddress[global03] : # 43| valnum = r43_6 -# 43| r43_7(int) = Load : &:r43_6, ~m39_12 +# 43| r43_7(int) = Load : &:r43_6, ~m39_2 # 43| valnum = unique # 43| r43_8(int) = Add : r43_5, r43_7 # 43| valnum = r43_8 @@ -310,7 +308,7 @@ test.cpp: # 44| valnum = m39_9 # 44| m44_5(int) = Store : &:r44_4, r44_1 # 44| valnum = r44_1 -# 44| m44_6(unknown) = Chi : total:m39_12, partial:m44_5 +# 44| m44_6(unknown) = Chi : total:m39_11, partial:m44_5 # 44| valnum = unique # 45| r45_1(glval) = VariableAddress[p0] : # 45| valnum = r39_4 @@ -324,7 +322,7 @@ test.cpp: # 45| valnum = r43_5 # 45| r45_6(glval) = VariableAddress[global03] : # 45| valnum = r43_6 -# 45| r45_7(int) = Load : &:r45_6, ~m44_6 +# 45| r45_7(int) = Load : &:r45_6, ~m39_2 # 45| valnum = unique # 45| r45_8(int) = Add : r45_5, r45_7 # 45| valnum = r45_8 @@ -341,13 +339,13 @@ test.cpp: # 46| m46_4(int) = Store : &:r46_3, r46_2 # 46| valnum = r45_8 # 47| v47_1(void) = NoOp : -# 39| v39_13(void) = ReturnIndirection : &:r39_10, ~m44_6 -# 39| r39_14(glval) = VariableAddress[#return] : +# 39| v39_12(void) = ReturnIndirection : &:r39_10, ~m44_6 +# 39| r39_13(glval) = VariableAddress[#return] : # 39| valnum = unique -# 39| v39_15(void) = ReturnValue : &:r39_14 -# 39| v39_16(void) = UnmodeledUse : mu* -# 39| v39_17(void) = AliasedUse : ~m44_6 -# 39| v39_18(void) = ExitFunction : +# 39| v39_14(void) = ReturnValue : &:r39_13 +# 39| v39_15(void) = UnmodeledUse : mu* +# 39| v39_16(void) = AliasedUse : ~m39_2 +# 39| v39_17(void) = ExitFunction : # 49| unsigned int my_strspn(char const*, char const*) # 49| Block 0 @@ -364,17 +362,13 @@ test.cpp: # 49| valnum = m49_5 # 49| m49_7(unknown) = InitializeIndirection[str] : &:r49_6 # 49| valnum = unique -# 49| m49_8(unknown) = Chi : total:m49_2, partial:m49_7 -# 49| valnum = unique -# 49| r49_9(glval) = VariableAddress[chars] : -# 49| valnum = r49_9 -# 49| m49_10(char *) = InitializeParameter[chars] : &:r49_9 -# 49| valnum = m49_10 -# 49| r49_11(char *) = Load : &:r49_9, m49_10 -# 49| valnum = m49_10 -# 49| m49_12(unknown) = InitializeIndirection[chars] : &:r49_11 -# 49| valnum = unique -# 49| m49_13(unknown) = Chi : total:m49_8, partial:m49_12 +# 49| r49_8(glval) = VariableAddress[chars] : +# 49| valnum = r49_8 +# 49| m49_9(char *) = InitializeParameter[chars] : &:r49_8 +# 49| valnum = m49_9 +# 49| r49_10(char *) = Load : &:r49_8, m49_9 +# 49| valnum = m49_9 +# 49| m49_11(unknown) = InitializeIndirection[chars] : &:r49_10 # 49| valnum = unique # 50| r50_1(glval) = VariableAddress[ptr] : # 50| valnum = r50_1 @@ -395,7 +389,7 @@ test.cpp: # 53| valnum = r49_4 # 53| r53_3(char *) = Load : &:r53_2, m49_5 # 53| valnum = m49_5 -# 53| r53_4(char) = Load : &:r53_3, ~m49_13 +# 53| r53_4(char) = Load : &:r53_3, ~m49_7 # 53| valnum = unique # 53| r53_5(int) = Convert : r53_4 # 53| valnum = unique @@ -409,13 +403,13 @@ test.cpp: # 55| Block 2 # 55| r55_1(glval) = VariableAddress[chars] : -# 55| valnum = r49_9 -# 55| r55_2(char *) = Load : &:r55_1, m49_10 -# 55| valnum = m49_10 +# 55| valnum = r49_8 +# 55| r55_2(char *) = Load : &:r55_1, m49_9 +# 55| valnum = m49_9 # 55| r55_3(glval) = VariableAddress[ptr] : # 55| valnum = r50_1 # 55| m55_4(char *) = Store : &:r55_3, r55_2 -# 55| valnum = m49_10 +# 55| valnum = m49_9 #-----| Goto -> Block 3 # 56| Block 3 @@ -425,7 +419,7 @@ test.cpp: # 56| valnum = r50_1 # 56| r56_3(char *) = Load : &:r56_2, m56_1 # 56| valnum = m56_1 -# 56| r56_4(char) = Load : &:r56_3, ~m49_13 +# 56| r56_4(char) = Load : &:r56_3, ~m49_2 # 56| valnum = unique # 56| r56_5(int) = Convert : r56_4 # 56| valnum = unique @@ -433,7 +427,7 @@ test.cpp: # 56| valnum = r49_4 # 56| r56_7(char *) = Load : &:r56_6, m49_5 # 56| valnum = m49_5 -# 56| r56_8(char) = Load : &:r56_7, ~m49_13 +# 56| r56_8(char) = Load : &:r56_7, ~m49_7 # 56| valnum = unique # 56| r56_9(int) = Convert : r56_8 # 56| valnum = unique @@ -448,7 +442,7 @@ test.cpp: # 56| valnum = r50_1 # 56| r56_13(char *) = Load : &:r56_12, m56_1 # 56| valnum = m56_1 -# 56| r56_14(char) = Load : &:r56_13, ~m49_13 +# 56| r56_14(char) = Load : &:r56_13, ~m49_2 # 56| valnum = unique # 56| r56_15(int) = Convert : r56_14 # 56| valnum = unique @@ -478,7 +472,7 @@ test.cpp: # 59| valnum = r50_1 # 59| r59_2(char *) = Load : &:r59_1, m56_1 # 59| valnum = m56_1 -# 59| r59_3(char) = Load : &:r59_2, ~m49_13 +# 59| r59_3(char) = Load : &:r59_2, ~m49_2 # 59| valnum = unique # 59| r59_4(int) = Convert : r59_3 # 59| valnum = unique @@ -517,14 +511,14 @@ test.cpp: # 65| valnum = m53_1 # 65| m65_4(unsigned int) = Store : &:r65_1, r65_3 # 65| valnum = m53_1 -# 49| v49_14(void) = ReturnIndirection : &:r49_6, ~m49_13 -# 49| v49_15(void) = ReturnIndirection : &:r49_11, ~m49_13 -# 49| r49_16(glval) = VariableAddress[#return] : +# 49| v49_12(void) = ReturnIndirection : &:r49_6, m49_7 +# 49| v49_13(void) = ReturnIndirection : &:r49_10, m49_11 +# 49| r49_14(glval) = VariableAddress[#return] : # 49| valnum = r65_1 -# 49| v49_17(void) = ReturnValue : &:r49_16, m65_4 -# 49| v49_18(void) = UnmodeledUse : mu* -# 49| v49_19(void) = AliasedUse : ~m49_13 -# 49| v49_20(void) = ExitFunction : +# 49| v49_15(void) = ReturnValue : &:r49_14, m65_4 +# 49| v49_16(void) = UnmodeledUse : mu* +# 49| v49_17(void) = AliasedUse : ~m49_2 +# 49| v49_18(void) = ExitFunction : # 75| void test04(two_values*) # 75| Block 0 @@ -541,17 +535,15 @@ test.cpp: # 75| valnum = m75_5 # 75| m75_7(unknown) = InitializeIndirection[vals] : &:r75_6 # 75| valnum = unique -# 75| m75_8(unknown) = Chi : total:m75_2, partial:m75_7 -# 75| valnum = unique # 77| r77_1(glval) = VariableAddress[v] : # 77| valnum = r77_1 # 77| r77_2(glval) = FunctionAddress[getAValue] : # 77| valnum = unique # 77| r77_3(int) = Call : func:r77_2 # 77| valnum = unique -# 77| m77_4(unknown) = ^CallSideEffect : ~m75_8 +# 77| m77_4(unknown) = ^CallSideEffect : ~m75_2 # 77| valnum = unique -# 77| m77_5(unknown) = Chi : total:m75_8, partial:m77_4 +# 77| m77_5(unknown) = Chi : total:m75_2, partial:m77_4 # 77| valnum = unique # 77| r77_6(signed short) = Convert : r77_3 # 77| valnum = r77_6 @@ -569,7 +561,7 @@ test.cpp: # 79| valnum = m75_5 # 79| r79_6(glval) = FieldAddress[val1] : r79_5 # 79| valnum = unique -# 79| r79_7(signed short) = Load : &:r79_6, ~m77_5 +# 79| r79_7(signed short) = Load : &:r79_6, ~m75_7 # 79| valnum = unique # 79| r79_8(int) = Convert : r79_7 # 79| valnum = unique @@ -579,7 +571,7 @@ test.cpp: # 79| valnum = m75_5 # 79| r79_11(glval) = FieldAddress[val2] : r79_10 # 79| valnum = unique -# 79| r79_12(signed short) = Load : &:r79_11, ~m77_5 +# 79| r79_12(signed short) = Load : &:r79_11, ~m75_7 # 79| valnum = unique # 79| r79_13(int) = Convert : r79_12 # 79| valnum = unique @@ -612,11 +604,11 @@ test.cpp: # 82| m82_1(unknown) = Phi : from 0:~m77_5, from 1:~m80_4 # 82| valnum = unique # 82| v82_2(void) = NoOp : -# 75| v75_9(void) = ReturnIndirection : &:r75_6, ~m82_1 -# 75| v75_10(void) = ReturnVoid : -# 75| v75_11(void) = UnmodeledUse : mu* -# 75| v75_12(void) = AliasedUse : ~m82_1 -# 75| v75_13(void) = ExitFunction : +# 75| v75_8(void) = ReturnIndirection : &:r75_6, m75_7 +# 75| v75_9(void) = ReturnVoid : +# 75| v75_10(void) = UnmodeledUse : mu* +# 75| v75_11(void) = AliasedUse : ~m82_1 +# 75| v75_12(void) = ExitFunction : # 84| void test05(int, int, void*) # 84| Block 0 @@ -641,8 +633,6 @@ test.cpp: # 84| valnum = m84_9 # 84| m84_11(unknown) = InitializeIndirection[p] : &:r84_10 # 84| valnum = unique -# 84| m84_12(unknown) = Chi : total:m84_2, partial:m84_11 -# 84| valnum = unique # 86| r86_1(glval) = VariableAddress[v] : # 86| valnum = r86_1 # 86| m86_2(int) = Uninitialized[v] : &:r86_1 @@ -671,11 +661,11 @@ test.cpp: # 88| m88_10(int) = Store : &:r88_9, r88_8 # 88| valnum = m88_6 # 89| v89_1(void) = NoOp : -# 84| v84_13(void) = ReturnIndirection : &:r84_10, ~m84_12 -# 84| v84_14(void) = ReturnVoid : -# 84| v84_15(void) = UnmodeledUse : mu* -# 84| v84_16(void) = AliasedUse : ~m84_12 -# 84| v84_17(void) = ExitFunction : +# 84| v84_12(void) = ReturnIndirection : &:r84_10, m84_11 +# 84| v84_13(void) = ReturnVoid : +# 84| v84_14(void) = UnmodeledUse : mu* +# 84| v84_15(void) = AliasedUse : ~m84_2 +# 84| v84_16(void) = ExitFunction : # 88| Block 2 # 88| r88_11(glval) = VariableAddress[x] : @@ -748,8 +738,6 @@ test.cpp: # 104| valnum = m104_5 # 104| m104_7(unknown) = InitializeIndirection[pd] : &:r104_6 # 104| valnum = unique -# 104| m104_8(unknown) = Chi : total:m104_2, partial:m104_7 -# 104| valnum = unique # 105| r105_1(glval) = VariableAddress[x] : # 105| valnum = unique # 105| r105_2(glval) = VariableAddress[pd] : @@ -760,7 +748,7 @@ test.cpp: # 105| valnum = r105_4 # 105| r105_5(glval) = FieldAddress[b] : r105_4 # 105| valnum = r105_5 -# 105| r105_6(int) = Load : &:r105_5, ~m104_8 +# 105| r105_6(int) = Load : &:r105_5, ~m104_7 # 105| valnum = r105_6 # 105| m105_7(int) = Store : &:r105_1, r105_6 # 105| valnum = r105_6 @@ -782,7 +770,7 @@ test.cpp: # 107| valnum = r105_4 # 107| r107_4(glval) = FieldAddress[b] : r107_3 # 107| valnum = r105_5 -# 107| r107_5(int) = Load : &:r107_4, ~m104_8 +# 107| r107_5(int) = Load : &:r107_4, ~m104_7 # 107| valnum = r107_5 # 107| m107_6(int) = Store : &:r107_1, r107_5 # 107| valnum = r107_5 @@ -794,13 +782,13 @@ test.cpp: # 109| valnum = r107_5 # 109| m109_4(int) = Store : &:r109_1, r109_3 # 109| valnum = r107_5 -# 104| v104_9(void) = ReturnIndirection : &:r104_6, ~m104_8 -# 104| r104_10(glval) = VariableAddress[#return] : +# 104| v104_8(void) = ReturnIndirection : &:r104_6, m104_7 +# 104| r104_9(glval) = VariableAddress[#return] : # 104| valnum = r109_1 -# 104| v104_11(void) = ReturnValue : &:r104_10, m109_4 -# 104| v104_12(void) = UnmodeledUse : mu* -# 104| v104_13(void) = AliasedUse : ~m104_8 -# 104| v104_14(void) = ExitFunction : +# 104| v104_10(void) = ReturnValue : &:r104_9, m109_4 +# 104| v104_11(void) = UnmodeledUse : mu* +# 104| v104_12(void) = AliasedUse : ~m104_2 +# 104| v104_13(void) = ExitFunction : # 112| void test06() # 112| Block 0 diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll index f5c6de314ab..eac4d333afc 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll @@ -1,6 +1,7 @@ private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or + TEntireAllocationMemoryAccess() or TEscapedMemoryAccess() or TNonLocalMemoryAccess() or TPhiMemoryAccess() or @@ -43,6 +44,16 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess { final override predicate usesAddressOperand() { any() } } +/** + * The operand or results accesses all memory in the contiguous allocation that contains the address + * specified by the `AddressOperand` on the same instruction. + */ +class EntireAllocationMemoryAccess extends MemoryAccessKind, TEntireAllocationMemoryAccess { + override string toString() { result = "alloc" } + + final override predicate usesAddressOperand() { any() } +} + /** * The operand or result accesses all memory whose address has escaped. */ diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll index d39d8c30a1a..4b1124cf27e 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll @@ -232,6 +232,31 @@ abstract class BufferReadOpcode extends BufferAccessOpcode { final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess } } +/** + * An opcode that access an entire memory allocation. + */ +abstract class EntireAllocationAccessOpcode extends Opcode { + final override predicate hasAddressOperand() { any() } +} + +/** + * An opcode that write to an entire memory allocation. + */ +abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + +/** + * An opcode that reads from an entire memory allocation. + */ +abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + /** * An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`. */ @@ -325,7 +350,7 @@ module Opcode { final override string toString() { result = "InitializeParameter" } } - class InitializeIndirection extends IndirectWriteOpcode, TInitializeIndirection { + class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } @@ -349,7 +374,7 @@ module Opcode { final override string toString() { result = "ReturnVoid" } } - class ReturnIndirection extends IndirectReadOpcode, TReturnIndirection { + class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } final override predicate hasOperandInternal(OperandTag tag) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 6b73b601737..e2d3828fc52 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -1,40 +1,11 @@ private import AliasAnalysisInternal -private import csharp private import InputIR -private import semmle.code.csharp.ir.internal.IntegerConstant as Ints +private import AliasAnalysisImports private class IntValue = Ints::IntValue; /** - * Converts the bit count in `bits` to a byte count and a bit count in the form - * bytes:bits. - */ -bindingset[bits] -string bitsToBytesAndBits(int bits) { result = (bits / 8).toString() + ":" + (bits % 8).toString() } - -/** - * Gets a printable string for a bit offset with possibly unknown value. - */ -bindingset[bitOffset] -string getBitOffsetString(IntValue bitOffset) { - if Ints::hasValue(bitOffset) - then - if bitOffset >= 0 - then result = "+" + bitsToBytesAndBits(bitOffset) - else result = "-" + bitsToBytesAndBits(Ints::neg(bitOffset)) - else result = "+?" -} - -///** -// * Gets the offset of field `field` in bits. -// */ -//private IntValue getFieldBitOffset(Field field) { -// if field instanceof BitField -// then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset()) -// else result = Ints::mul(field.getByteOffset(), 8) -//} -/** - * Holds if the operand `operand` of instruction `instr` is used in a way that does + * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the * instruction. */ @@ -51,6 +22,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Neither operand of a PointerDiff escapes. instr instanceof PointerDiffInstruction + or + // Converting an address to a `bool` does not escape the address. + instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType ) ) or @@ -64,6 +38,7 @@ private predicate operandEscapesDomain(Operand operand) { not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and + not operand.getUse() instanceof ReturnIndirectionInstruction and not operand instanceof PhiInputOperand } @@ -94,7 +69,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `operand` of instruction `instr` is + * Holds if any address held in operand `tag` of instruction `instr` is * propagated to the result of `instr`, offset by the number of bits in * `bitOffset`. If the address is propagated, but the offset is not known to be * a constant, then `bitOffset` is unknown. @@ -103,48 +78,46 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { exists(Instruction instr | instr = operand.getUse() and ( - // REVIEW: See the REVIEW comment bellow - // // Converting to a non-virtual base class adds the offset of the base class. - // exists(ConvertToNonVirtualBaseInstruction convert | - // convert = instr and - // bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - // ) - // or - // // Converting to a derived class subtracts the offset of the base class. - // exists(ConvertToDerivedInstruction convert | - // convert = instr and - // bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - // ) - // or - // // Converting to a virtual base class adds an unknown offset. - // instr instanceof ConvertToVirtualBaseInstruction and - // bitOffset = Ints::unknown() - // or - // REVIEW: In the C# IR, we should ignore the above types of conversion all together, - // since first of all they do not provide correct information (nothing is known - // for sure about heap allocated objects) and second of all even if we create a - // virtual memory model for the IR I don't think such conversions provide any meaningful - // information; - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, Type resultType | + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | convert = instr and - resultType = convert.getResultType() and - ( - resultType instanceof PointerType or - resultType instanceof RefType - ) and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and bitOffset = 0 ) or // Adding an integer to or subtracting an integer from a pointer propagates // the address with an offset. - bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) or // Computing a field address from a pointer propagates the address plus the // offset of the field. - // TODO: Fix once class layout is synthesized - // bitOffset = Ints::unknown() - //or + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or // A copy propagates the source value. operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or @@ -224,27 +197,42 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { } private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { - exists(Callable c | + exists(Language::Function f | ci = operand.getUse() and - c = ci.getStaticCallTarget() and + f = ci.getStaticCallTarget() and ( init.(InitializeParameterInstruction).getParameter() = - c.getParameter(operand.(PositionalArgumentOperand).getIndex()) + f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or init instanceof InitializeThisInstruction and - init.getEnclosingFunction() = c and + init.getEnclosingFunction() = f and operand instanceof ThisArgumentOperand - ) // and - // not f.isVirtual() and - // not f instanceof AliasFunction + ) and + not Language::isFunctionVirtual(f) and + not f instanceof AliasModels::AliasFunction ) } -private predicate isAlwaysReturnedArgument(Operand operand) { none() } +private predicate isAlwaysReturnedArgument(Operand operand) { + exists(AliasModels::AliasFunction f | + f = operand.getUse().(CallInstruction).getStaticCallTarget() and + f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) + ) +} -private predicate isOnlyEscapesViaReturnArgument(Operand operand) { none() } +private predicate isOnlyEscapesViaReturnArgument(Operand operand) { + exists(AliasModels::AliasFunction f | + f = operand.getUse().(CallInstruction).getStaticCallTarget() and + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ) +} -private predicate isNeverEscapesArgument(Operand operand) { none() } +private predicate isNeverEscapesArgument(Operand operand) { + exists(AliasModels::AliasFunction f | + f = operand.getUse().(CallInstruction).getStaticCallTarget() and + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ) +} private predicate resultReturned(Instruction instr, IntValue bitOffset) { operandReturned(instr.getAUse(), bitOffset) @@ -262,52 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) { } /** - * Holds if the address of the specified local variable or parameter escapes the - * domain of the analysis. + * Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur + * either because the allocation's address is taken within the function and escapes, or because the + * allocation is marked as always escaping via `alwaysEscapes()`. */ -private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { - // The variable's address escapes if the result of any - // VariableAddressInstruction that computes the variable's address escapes. - exists(VariableAddressInstruction instr | - instr.getIRVariable() = var and - resultEscapesNonReturn(instr) +predicate allocationEscapes(Configuration::Allocation allocation) { + allocation.alwaysEscapes() + or + exists(IREscapeAnalysisConfiguration config | + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) } /** - * Holds if the address of the specified variable escapes the domain of the - * analysis. + * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -predicate variableAddressEscapes(IRVariable var) { - automaticVariableAddressEscapes(var.(IRAutomaticVariable)) +private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { + operandIsPropagated(operand, bitOffset) or - // All variables with static storage duration have their address escape. - not var instanceof IRAutomaticVariable + exists(CallInstruction call, Instruction init | + isArgumentForParameter(call, operand, init) and + resultReturned(init, bitOffset) + ) } /** - * Holds if the result of instruction `instr` points within variable `var`, at - * bit offset `bitOffset` within the variable. If the result points within - * `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown. + * Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset + * may be `unknown()`. */ -predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) { - // The address of a variable points to that variable, at offset 0. - instr.(VariableAddressInstruction).getIRVariable() = var and - bitOffset = 0 +private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) { + base = addrOperand.getDef() and bitOffset = 0 // Base case or - exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset | - operand = instr.getAnOperand() and - // If an operand is propagated, then the result points to the same variable, - // offset by the bit offset from the propagation. - resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and - ( - operandIsPropagated(operand, propagatedBitOffset) - or - exists(CallInstruction ci, Instruction init | - isArgumentForParameter(ci, operand, init) and - resultReturned(init, propagatedBitOffset) - ) - ) and - bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) + exists( + Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset + | + // We already have an offset from `middle`. + hasBaseAndOffset(addrOperand, middle, previousBitOffset) and + // `middle` is propagated from `base`. + middleOperand = middle.getAnOperand() and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + base = middleOperand.getDef() and + bitOffset = Ints::add(previousBitOffset, additionalBitOffset) + ) +} + +/** + * Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`. + * Only holds for the `base` with the longest chain of propagation to `addrOperand`. + */ +predicate addressOperandBaseAndConstantOffset( + AddressOperand addrOperand, Instruction base, int bitOffset +) { + hasBaseAndOffset(addrOperand, base, bitOffset) and + Ints::hasValue(bitOffset) and + not exists(Instruction previousBase, int previousBitOffset | + hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and + previousBase = base.getAnOperand().getDef() and + Ints::hasValue(previousBitOffset) + ) +} + +/** + * Gets the allocation into which `addrOperand` points, if known. + */ +Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) { + addressOperandAllocationAndOffset(addrOperand, result, _) +} + +/** + * Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The + * offset may be `unknown()`. + */ +predicate addressOperandAllocationAndOffset( + AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset +) { + exists(Instruction base | + allocation.getABaseInstruction() = base and + hasBaseAndOffset(addrOperand, base, bitOffset) and + not exists(Instruction previousBase | + hasBaseAndOffset(addrOperand, previousBase, _) and + previousBase = base.getAnOperand().getDef() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll new file mode 100644 index 00000000000..11d7d37063e --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -0,0 +1,48 @@ +private import csharp +import semmle.code.csharp.ir.implementation.IRConfiguration +import semmle.code.csharp.ir.internal.IntegerConstant as Ints + +module AliasModels { + /** + * Models the aliasing behavior of a library function. + */ + abstract class AliasFunction extends Callable { + /** + * Holds if the address passed to the parameter at the specified index is never retained after + * the function returns. + * + * Example: + * ``` + * int* g; + * int* func(int* p, int* q, int* r, int* s, int n) { + * *s = 1; // `s` does not escape. + * g = p; // Stored in global. `p` escapes. + * if (rand()) { + * return q; // `q` escapes via the return value. + * } + * else { + * return r + n; // `r` escapes via the return value, even though an offset has been added. + * } + * } + * ``` + * + * For the above function, the following terms hold: + * - `parameterEscapesOnlyViaReturn(1)` + * - `parameterEscapesOnlyViaReturn(2)` + * - `parameterNeverEscapes(3)` + */ + abstract predicate parameterNeverEscapes(int index); + + /** + * Holds if the address passed to the parameter at the specified index escapes via the return + * value of the function, but does not otherwise escape. See the comment for + * `parameterNeverEscapes` for an example. + */ + abstract predicate parameterEscapesOnlyViaReturn(int index); + + /** + * Holds if the function always returns the value of the parameter at the specified index. + */ + abstract predicate parameterIsAlwaysReturned(int index); + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll index 64d671c76c4..e7884ce20b6 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll @@ -1 +1,3 @@ +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language import semmle.code.csharp.ir.implementation.raw.IR as InputIR +import AliasConfiguration as Configuration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll new file mode 100644 index 00000000000..5be476e12ee --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -0,0 +1,16 @@ +private import AliasConfigurationImports + +/** + * A memory allocation that can be tracked by the SimpleSSA alias analysis. + * All automatic variables are tracked. + */ +class Allocation extends IRAutomaticVariable { + VariableAddressInstruction getABaseInstruction() { result.getIRVariable() = this } + + final string getAllocationString() { result = toString() } + + predicate alwaysEscapes() { + // An automatic variable only escapes if its address is taken and escapes. + none() + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll new file mode 100644 index 00000000000..0db2b956610 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.implementation.raw.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index ddff6444b90..7de1ab8d72e 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -1,66 +1,57 @@ import AliasAnalysis private import SimpleSSAImports import SimpleSSAPublicImports +private import AliasConfiguration -private class IntValue = Ints::IntValue; - -private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset -) { - resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and - type = instr.getResultLanguageType() -} - -private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset -) { - resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and - type = operand.getLanguageType() -} - -/** - * 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, Language::LanguageType type, IntValue bitOffset | - hasResultMemoryAccess(instr, var, type, bitOffset) and - not instr.hasResultMayMemoryAccess() - | +private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRType type) { + exists(Instruction constantBase, int bitOffset | + addressOperandBaseAndConstantOffset(addrOperand, constantBase, bitOffset) and bitOffset = 0 and - type.getIRType() = var.getIRType() and - not instr.hasResultMayMemoryAccess() - ) and - forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset | - hasOperandMemoryAccess(operand, var, type, bitOffset) - | - bitOffset = 0 and - type.getIRType() = var.getIRType() and - not operand.hasMayReadMemoryAccess() + constantBase = var.getABaseInstruction() and + type = var.getIRType() ) } -private newtype TMemoryLocation = MkMemoryLocation(IRVariable var) { isVariableModeled(var) } +/** + * 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(Allocation var) { + not allocationEscapes(var) and + forall(Instruction instr, AddressOperand addrOperand, IRType type | + addrOperand = instr.getResultAddressOperand() and + type = instr.getResultIRType() and + var = getAddressOperandAllocation(addrOperand) + | + isTotalAccess(var, addrOperand, type) and not instr.hasResultMayMemoryAccess() + ) and + forall(MemoryOperand memOperand, AddressOperand addrOperand, IRType type | + addrOperand = memOperand.getAddressOperand() and + type = memOperand.getIRType() and + var = getAddressOperandAllocation(addrOperand) + | + isTotalAccess(var, addrOperand, type) and not memOperand.hasMayReadMemoryAccess() + ) +} -private MemoryLocation getMemoryLocation(IRVariable var) { result.getIRVariable() = var } +private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } + +private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } class MemoryLocation extends TMemoryLocation { - IRVariable var; + Allocation var; MemoryLocation() { this = MkMemoryLocation(var) } - final string toString() { result = var.toString() } + final string toString() { result = var.getAllocationString() } + + final Allocation getAllocation() { result = var } final Language::Location getLocation() { result = var.getLocation() } final IRFunction getIRFunction() { result = var.getEnclosingIRFunction() } - final IRVariable getIRVariable() { result = var } - final VirtualVariable getVirtualVariable() { result = this } final Language::LanguageType getType() { result = var.getLanguageType() } @@ -77,15 +68,9 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) { } MemoryLocation getResultMemoryLocation(Instruction instr) { - exists(IRVariable var | - hasResultMemoryAccess(instr, var, _, _) and - result = getMemoryLocation(var) - ) + result = getMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand())) } MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { - exists(IRVariable var | - hasOperandMemoryAccess(operand, var, _, _) and - result = getMemoryLocation(var) - ) + result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand())) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll index 97121f46453..cd4e96a25a3 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll @@ -101,3 +101,16 @@ predicate hasPotentialLoop(Function f) { } predicate hasGoto(Function f) { exists(CSharp::GotoStmt s | s.getEnclosingCallable() = f) } + +/** + * Gets the offset of field `field` in bits. + */ +int getFieldBitOffset(Field f) { + //REVIEW: Implement this once layout has been synthesized. + none() +} + +/** + * Holds if the specified `Function` can be overridden in a derived class. + */ +predicate isFunctionVirtual(Function f) { f.(CSharp::Virtualizable).isOverridableOrImplementable() }