diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index 4faa1b1aae92..7321d82ebc77 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -183,7 +183,7 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) { i2.(UnaryInstruction).getUnary() = i1 or i2.(ChiInstruction).getPartial() = i1 and - not isChiForAllAliasedMemory(i2) + not i2.isResultConflated() or exists(BinaryInstruction bin | bin = i2 and @@ -276,19 +276,6 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet ) } -/** - * Holds if `chi` is on the chain of chi-instructions for all aliased memory. - * Taint should not pass through these instructions since they tend to mix up - * unrelated objects. - */ -private predicate isChiForAllAliasedMemory(Instruction instr) { - instr.(ChiInstruction).getTotal() instanceof AliasedDefinitionInstruction - or - isChiForAllAliasedMemory(instr.(ChiInstruction).getTotal()) - or - isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) -} - private predicate modelTaintToReturnValue(Function f, int parameterIn) { // Taint flow from parameter to return value exists(FunctionInput modelIn, FunctionOutput modelOut | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll index 23850ebd7ab2..94d0192fe189 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll @@ -274,6 +274,36 @@ module InstructionSanity { funcText = Language::getIdentityString(func.getFunction()) } + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr instanceof UnmodeledDefinitionInstruction + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated(Instruction instr) { + shouldBeConflated(instr) and + not instr.isResultConflated() + } + + query predicate wronglyMarkedAsConflated(Instruction instr) { + instr.isResultConflated() and + not shouldBeConflated(instr) + } + query predicate invalidOverlap( MemoryOperand useOperand, string message, IRFunction func, string funcText ) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 72c8d7e3b95a..1e84d1c34759 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -321,6 +321,17 @@ class Instruction extends Construction::TInstruction { Construction::hasModeledMemoryResult(this) } + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + /** * Gets the successor of this instruction along the control flow edge * specified by `kind`. 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 e754e8d3849f..9cd0384f924c 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 @@ -354,6 +354,7 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation { final override predicate isMayAccess() { isMayAccess = true } } +/** A virtual variable that groups all escaped memory within a function. */ class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable { AliasedVirtualVariable() { not isMayAccess() } } @@ -429,10 +430,18 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) { 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 + if def.getAllocation() = use.getAllocation() + then + // EntireAllocationMemoryLocation totally overlaps any location within + // the same allocation. + result instanceof MustTotallyOverlap + else ( + // There is no overlap with a location that's known to belong to a + // different allocation, but all other locations may partially overlap. + not exists(use.getAllocation()) and + result instanceof MayPartiallyOverlap + ) ) or exists(VariableMemoryLocation defVariableLocation | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 0d158023ad49..155934689b6c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -65,6 +65,29 @@ private module Cached { instruction instanceof ChiInstruction // Chis always have modeled results } + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof UnmodeledDefinitionInstruction + or + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + or + // Chi instructions track virtual variables, and therefore a chi instruction is + // conflated if it's associated with the aliased virtual variable. + exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof + Alias::AliasedVirtualVariable + ) + or + // Phi instructions track locations, and therefore a phi instruction is + // conflated if it's associated with a conflated location. + exists(Alias::MemoryLocation location | + instruction = Phi(_, location) and + not exists(location.getAllocation()) + ) + } + cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll index 23850ebd7ab2..94d0192fe189 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll @@ -274,6 +274,36 @@ module InstructionSanity { funcText = Language::getIdentityString(func.getFunction()) } + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr instanceof UnmodeledDefinitionInstruction + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated(Instruction instr) { + shouldBeConflated(instr) and + not instr.isResultConflated() + } + + query predicate wronglyMarkedAsConflated(Instruction instr) { + instr.isResultConflated() and + not shouldBeConflated(instr) + } + query predicate invalidOverlap( MemoryOperand useOperand, string message, IRFunction func, string funcText ) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 72c8d7e3b95a..1e84d1c34759 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -321,6 +321,17 @@ class Instruction extends Construction::TInstruction { Construction::hasModeledMemoryResult(this) } + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + /** * Gets the successor of this instruction along the control flow edge * specified by `kind`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index c2fe485ec84c..18a77452929a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -61,6 +61,15 @@ private module Cached { cached predicate hasModeledMemoryResult(Instruction instruction) { none() } + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof UnmodeledDefinitionInstruction + or + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + } + cached Expr getInstructionConvertedResultExpression(Instruction instruction) { exists(TranslatedExpr translatedExpr | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll index 23850ebd7ab2..94d0192fe189 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll @@ -274,6 +274,36 @@ module InstructionSanity { funcText = Language::getIdentityString(func.getFunction()) } + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr instanceof UnmodeledDefinitionInstruction + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated(Instruction instr) { + shouldBeConflated(instr) and + not instr.isResultConflated() + } + + query predicate wronglyMarkedAsConflated(Instruction instr) { + instr.isResultConflated() and + not shouldBeConflated(instr) + } + query predicate invalidOverlap( MemoryOperand useOperand, string message, IRFunction func, string funcText ) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 72c8d7e3b95a..1e84d1c34759 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -321,6 +321,17 @@ class Instruction extends Construction::TInstruction { Construction::hasModeledMemoryResult(this) } + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + /** * Gets the successor of this instruction along the control flow edge * specified by `kind`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 0d158023ad49..155934689b6c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,6 +65,29 @@ private module Cached { instruction instanceof ChiInstruction // Chis always have modeled results } + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof UnmodeledDefinitionInstruction + or + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + or + // Chi instructions track virtual variables, and therefore a chi instruction is + // conflated if it's associated with the aliased virtual variable. + exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof + Alias::AliasedVirtualVariable + ) + or + // Phi instructions track locations, and therefore a phi instruction is + // conflated if it's associated with a conflated location. + exists(Alias::MemoryLocation location | + instruction = Phi(_, location) and + not exists(location.getAllocation()) + ) + } + cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand | 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 7de1ab8d72ef..aee4959513ed 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 @@ -61,6 +61,11 @@ class MemoryLocation extends TMemoryLocation { class VirtualVariable extends MemoryLocation { } +/** A virtual variable that groups all escaped memory within a function. */ +class AliasedVirtualVariable extends VirtualVariable { + AliasedVirtualVariable() { none() } +} + Overlap getOverlap(MemoryLocation def, MemoryLocation use) { def = use and result instanceof MustExactlyOverlap or diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected index 187d2750c8b5..735555b5a4b1 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected @@ -20,6 +20,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected index 187d2750c8b5..735555b5a4b1 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected @@ -20,6 +20,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected index 187d2750c8b5..735555b5a4b1 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected @@ -20,6 +20,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected index 187d2750c8b5..735555b5a4b1 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected @@ -20,6 +20,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity_unsound.expected index 187d2750c8b5..735555b5a4b1 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity_unsound.expected @@ -20,6 +20,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes 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 a3ab534a3952..e271e7bfff3e 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 @@ -824,41 +824,43 @@ ssa.cpp: # 184| m184_7(unsigned int &) = InitializeParameter[a] : &:r184_6 # 184| r184_8(unsigned int &) = Load : &:r184_6, m184_7 # 184| m184_9(unknown) = InitializeIndirection[a] : &:r184_8 -# 184| r184_10(glval) = VariableAddress[b] : -# 184| m184_11(unsigned int &) = InitializeParameter[b] : &:r184_10 -# 184| r184_12(unsigned int &) = Load : &:r184_10, m184_11 -# 184| m184_13(unknown) = InitializeIndirection[b] : &:r184_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| r184_18(glval) = VariableAddress[d] : -# 184| m184_19(unsigned int &) = InitializeParameter[d] : &:r184_18 -# 184| r184_20(unsigned int &) = Load : &:r184_18, m184_19 -# 184| m184_21(unknown) = InitializeIndirection[d] : &:r184_20 +# 184| m184_10(unknown) = Chi : total:m184_4, partial:m184_9 +# 184| r184_11(glval) = VariableAddress[b] : +# 184| m184_12(unsigned int &) = InitializeParameter[b] : &:r184_11 +# 184| r184_13(unsigned int &) = Load : &:r184_11, m184_12 +# 184| m184_14(unknown) = InitializeIndirection[b] : &:r184_13 +# 184| m184_15(unknown) = Chi : total:m184_10, partial:m184_14 +# 184| r184_16(glval) = VariableAddress[c] : +# 184| m184_17(unsigned int &) = InitializeParameter[c] : &:r184_16 +# 184| r184_18(unsigned int &) = Load : &:r184_16, m184_17 +# 184| m184_19(unknown) = InitializeIndirection[c] : &:r184_18 +# 184| r184_20(glval) = VariableAddress[d] : +# 184| m184_21(unsigned int &) = InitializeParameter[d] : &:r184_20 +# 184| r184_22(unsigned int &) = Load : &:r184_20, m184_21 +# 184| m184_23(unknown) = InitializeIndirection[d] : &:r184_22 # 189| r189_1(glval) = VariableAddress[a] : # 189| r189_2(unsigned int &) = Load : &:r189_1, m184_7 # 189| r189_3(glval) = CopyValue : r189_2 # 189| r189_4(glval) = VariableAddress[b] : -# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_11 +# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_12 # 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_17 +# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_17 +# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_19 # 190| r190_4(glval) = VariableAddress[d] : -# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_19 -# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_21 -# 186| m186_1(unknown) = InlineAsm : ~m184_13, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 -# 186| m186_2(unknown) = Chi : total:m184_13, partial:m186_1 +# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_21 +# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23 +# 186| m186_1(unknown) = InlineAsm : ~m184_15, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6 +# 186| m186_2(unknown) = Chi : total:m184_15, partial:m186_1 # 192| v192_1(void) = NoOp : -# 184| v184_22(void) = ReturnIndirection : &:r184_8, ~m186_2 -# 184| v184_23(void) = ReturnIndirection : &:r184_12, ~m186_2 -# 184| v184_24(void) = ReturnIndirection : &:r184_16, m184_17 -# 184| v184_25(void) = ReturnIndirection : &:r184_20, m184_21 -# 184| v184_26(void) = ReturnVoid : -# 184| v184_27(void) = UnmodeledUse : mu* -# 184| v184_28(void) = AliasedUse : ~m186_2 -# 184| v184_29(void) = ExitFunction : +# 184| v184_24(void) = ReturnIndirection : &:r184_8, ~m186_2 +# 184| v184_25(void) = ReturnIndirection : &:r184_13, ~m186_2 +# 184| v184_26(void) = ReturnIndirection : &:r184_18, m184_19 +# 184| v184_27(void) = ReturnIndirection : &:r184_22, m184_23 +# 184| v184_28(void) = ReturnVoid : +# 184| v184_29(void) = UnmodeledUse : mu* +# 184| v184_30(void) = AliasedUse : ~m186_2 +# 184| v184_31(void) = ExitFunction : # 198| int PureFunctions(char*, char*, int) # 198| Block 0 @@ -1147,18 +1149,19 @@ ssa.cpp: # 247| m247_7(char *) = InitializeParameter[src] : &:r247_6 # 247| r247_8(char *) = Load : &:r247_6, m247_7 # 247| m247_9(unknown) = InitializeIndirection[src] : &:r247_8 -# 247| r247_10(glval) = VariableAddress[size] : -# 247| m247_11(int) = InitializeParameter[size] : &:r247_10 +# 247| m247_10(unknown) = Chi : total:m247_4, partial:m247_9 +# 247| r247_11(glval) = VariableAddress[size] : +# 247| m247_12(int) = InitializeParameter[size] : &:r247_11 # 248| r248_1(glval) = VariableAddress[dst] : # 248| r248_2(glval) = FunctionAddress[operator new[]] : # 248| r248_3(glval) = VariableAddress[size] : -# 248| r248_4(int) = Load : &:r248_3, m247_11 +# 248| r248_4(int) = Load : &:r248_3, m247_12 # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 # 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 -# 248| m248_9(unknown) = ^CallSideEffect : ~m247_9 -# 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9 +# 248| m248_9(unknown) = ^CallSideEffect : ~m247_10 +# 248| m248_10(unknown) = Chi : total:m247_10, partial:m248_9 # 248| r248_11(char *) = Convert : r248_8 # 248| m248_12(char *) = Store : &:r248_1, r248_11 # 249| r249_1(char) = Constant[97] : @@ -1175,7 +1178,7 @@ ssa.cpp: # 250| r250_6(char *) = Load : &:r250_5, m247_7 # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : -# 250| r250_9(int) = Load : &:r250_8, m247_11 +# 250| r250_9(int) = Load : &:r250_8, m247_12 # 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6 # 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 @@ -1184,12 +1187,12 @@ ssa.cpp: # 251| r251_2(glval) = VariableAddress[dst] : # 251| r251_3(char *) = Load : &:r251_2, m248_12 # 251| m251_4(char *) = Store : &:r251_1, r251_3 -# 247| v247_12(void) = ReturnIndirection : &:r247_8, ~m250_13 -# 247| r247_13(glval) = VariableAddress[#return] : -# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4 -# 247| v247_15(void) = UnmodeledUse : mu* -# 247| v247_16(void) = AliasedUse : ~m250_13 -# 247| v247_17(void) = ExitFunction : +# 247| v247_13(void) = ReturnIndirection : &:r247_8, ~m250_13 +# 247| r247_14(glval) = VariableAddress[#return] : +# 247| v247_15(void) = ReturnValue : &:r247_14, m251_4 +# 247| v247_16(void) = UnmodeledUse : mu* +# 247| v247_17(void) = AliasedUse : ~m250_13 +# 247| v247_18(void) = ExitFunction : # 254| char StringLiteralAliasing2(bool) # 254| Block 0 @@ -1250,35 +1253,95 @@ ssa.cpp: # 268| m268_7(void *) = InitializeParameter[s] : &:r268_6 # 268| r268_8(void *) = Load : &:r268_6, m268_7 # 268| m268_9(unknown) = InitializeIndirection[s] : &:r268_8 -# 268| r268_10(glval) = VariableAddress[size] : -# 268| m268_11(int) = InitializeParameter[size] : &:r268_10 +# 268| m268_10(unknown) = Chi : total:m268_4, partial:m268_9 +# 268| r268_11(glval) = VariableAddress[size] : +# 268| m268_12(int) = InitializeParameter[size] : &:r268_11 # 269| r269_1(glval) = VariableAddress[buf] : # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : -# 269| r269_4(int) = Load : &:r269_3, m268_11 +# 269| r269_4(int) = Load : &:r269_3, m268_12 # 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 -# 269| m269_6(unknown) = ^CallSideEffect : ~m268_9 -# 269| m269_7(unknown) = Chi : total:m268_9, partial:m269_6 +# 269| m269_6(unknown) = ^CallSideEffect : ~m268_10 +# 269| m269_7(unknown) = Chi : total:m268_10, partial:m269_6 # 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5 -# 269| m269_9(void *) = Store : &:r269_1, r269_5 +# 269| m269_9(unknown) = Chi : total:m269_7, partial:m269_8 +# 269| m269_10(void *) = Store : &:r269_1, r269_5 # 270| r270_1(glval) = FunctionAddress[memcpy] : # 270| r270_2(glval) = VariableAddress[buf] : -# 270| r270_3(void *) = Load : &:r270_2, m269_9 +# 270| r270_3(void *) = Load : &:r270_2, m269_10 # 270| r270_4(glval) = VariableAddress[s] : # 270| r270_5(void *) = Load : &:r270_4, m268_7 # 270| r270_6(glval) = VariableAddress[size] : -# 270| r270_7(int) = Load : &:r270_6, m268_11 +# 270| r270_7(int) = Load : &:r270_6, m268_12 # 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 -# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_8 +# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_7 # 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 -# 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10 +# 270| m270_11(unknown) = Chi : total:m269_9, partial:m270_10 # 271| r271_1(glval) = VariableAddress[#return] : # 271| r271_2(glval) = VariableAddress[buf] : -# 271| r271_3(void *) = Load : &:r271_2, m269_9 +# 271| r271_3(void *) = Load : &:r271_2, m269_10 # 271| m271_4(void *) = Store : &:r271_1, r271_3 -# 268| v268_12(void) = ReturnIndirection : &:r268_8, ~m270_11 -# 268| r268_13(glval) = VariableAddress[#return] : -# 268| v268_14(void) = ReturnValue : &:r268_13, m271_4 -# 268| v268_15(void) = UnmodeledUse : mu* -# 268| v268_16(void) = AliasedUse : ~m270_11 -# 268| v268_17(void) = ExitFunction : +# 268| v268_13(void) = ReturnIndirection : &:r268_8, ~m270_11 +# 268| r268_14(glval) = VariableAddress[#return] : +# 268| v268_15(void) = ReturnValue : &:r268_14, m271_4 +# 268| v268_16(void) = UnmodeledUse : mu* +# 268| v268_17(void) = AliasedUse : ~m270_11 +# 268| v268_18(void) = ExitFunction : + +# 275| void EscapedButNotConflated(bool, Point, int) +# 275| Block 0 +# 275| v275_1(void) = EnterFunction : +# 275| m275_2(unknown) = AliasedDefinition : +# 275| m275_3(unknown) = InitializeNonLocal : +# 275| m275_4(unknown) = Chi : total:m275_2, partial:m275_3 +# 275| mu275_5(unknown) = UnmodeledDefinition : +# 275| r275_6(glval) = VariableAddress[c] : +# 275| m275_7(bool) = InitializeParameter[c] : &:r275_6 +# 275| r275_8(glval) = VariableAddress[p] : +# 275| m275_9(Point) = InitializeParameter[p] : &:r275_8 +# 275| r275_10(glval) = VariableAddress[x1] : +# 275| m275_11(int) = InitializeParameter[x1] : &:r275_10 +# 276| r276_1(glval) = VariableAddress[a] : +# 276| m276_2(Point) = Uninitialized[a] : &:r276_1 +# 276| m276_3(unknown) = Chi : total:m275_4, partial:m276_2 +# 276| r276_4(glval) = FieldAddress[x] : r276_1 +# 276| r276_5(int) = Constant[0] : +# 276| m276_6(int) = Store : &:r276_4, r276_5 +# 276| m276_7(unknown) = Chi : total:m276_3, partial:m276_6 +# 276| r276_8(glval) = FieldAddress[y] : r276_1 +# 276| r276_9(int) = Constant[0] : +# 276| m276_10(int) = Store : &:r276_8, r276_9 +# 276| m276_11(unknown) = Chi : total:m276_7, partial:m276_10 +# 277| r277_1(glval) = VariableAddress[a] : +# 277| r277_2(Point *) = CopyValue : r277_1 +# 277| r277_3(glval) = VariableAddress[pp] : +# 277| m277_4(Point *) = Store : &:r277_3, r277_2 +# 277| m277_5(unknown) = Chi : total:m276_11, partial:m277_4 +# 278| r278_1(glval) = VariableAddress[c] : +# 278| r278_2(bool) = Load : &:r278_1, m275_7 +# 278| v278_3(void) = ConditionalBranch : r278_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 279| Block 1 +# 279| r279_1(glval) = VariableAddress[x1] : +# 279| r279_2(int) = Load : &:r279_1, m275_11 +# 279| r279_3(glval) = VariableAddress[a] : +# 279| r279_4(glval) = FieldAddress[x] : r279_3 +# 279| m279_5(int) = Store : &:r279_4, r279_2 +# 279| m279_6(unknown) = Chi : total:m277_5, partial:m279_5 +#-----| Goto -> Block 2 + +# 281| Block 2 +# 281| m281_1(int) = Phi : from 0:m276_6, from 1:m279_5 +# 281| m281_2(unknown) = Phi : from 0:~m277_5, from 1:~m279_6 +# 281| r281_3(glval) = VariableAddress[x] : +# 281| r281_4(glval) = VariableAddress[a] : +# 281| r281_5(glval) = FieldAddress[x] : r281_4 +# 281| r281_6(int) = Load : &:r281_5, m281_1 +# 281| m281_7(int) = Store : &:r281_3, r281_6 +# 282| v282_1(void) = NoOp : +# 275| v275_12(void) = ReturnVoid : +# 275| v275_13(void) = UnmodeledUse : mu* +# 275| v275_14(void) = AliasedUse : ~m281_2 +# 275| v275_15(void) = ExitFunction : 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 bb6225a93d8c..0566e36756b4 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 @@ -1277,3 +1277,60 @@ ssa.cpp: # 268| v268_15(void) = UnmodeledUse : mu* # 268| v268_16(void) = AliasedUse : ~m269_7 # 268| v268_17(void) = ExitFunction : + +# 275| void EscapedButNotConflated(bool, Point, int) +# 275| Block 0 +# 275| v275_1(void) = EnterFunction : +# 275| m275_2(unknown) = AliasedDefinition : +# 275| m275_3(unknown) = InitializeNonLocal : +# 275| m275_4(unknown) = Chi : total:m275_2, partial:m275_3 +# 275| mu275_5(unknown) = UnmodeledDefinition : +# 275| r275_6(glval) = VariableAddress[c] : +# 275| m275_7(bool) = InitializeParameter[c] : &:r275_6 +# 275| r275_8(glval) = VariableAddress[p] : +# 275| m275_9(Point) = InitializeParameter[p] : &:r275_8 +# 275| r275_10(glval) = VariableAddress[x1] : +# 275| m275_11(int) = InitializeParameter[x1] : &:r275_10 +# 276| r276_1(glval) = VariableAddress[a] : +# 276| m276_2(Point) = Uninitialized[a] : &:r276_1 +# 276| r276_3(glval) = FieldAddress[x] : r276_1 +# 276| r276_4(int) = Constant[0] : +# 276| m276_5(int) = Store : &:r276_3, r276_4 +# 276| m276_6(Point) = Chi : total:m276_2, partial:m276_5 +# 276| r276_7(glval) = FieldAddress[y] : r276_1 +# 276| r276_8(int) = Constant[0] : +# 276| m276_9(int) = Store : &:r276_7, r276_8 +# 276| m276_10(Point) = Chi : total:m276_6, partial:m276_9 +# 277| r277_1(glval) = VariableAddress[a] : +# 277| r277_2(Point *) = CopyValue : r277_1 +# 277| r277_3(glval) = VariableAddress[pp] : +# 277| m277_4(Point *) = Store : &:r277_3, r277_2 +# 277| m277_5(unknown) = Chi : total:m275_4, partial:m277_4 +# 278| r278_1(glval) = VariableAddress[c] : +# 278| r278_2(bool) = Load : &:r278_1, m275_7 +# 278| v278_3(void) = ConditionalBranch : r278_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 279| Block 1 +# 279| r279_1(glval) = VariableAddress[x1] : +# 279| r279_2(int) = Load : &:r279_1, m275_11 +# 279| r279_3(glval) = VariableAddress[a] : +# 279| r279_4(glval) = FieldAddress[x] : r279_3 +# 279| m279_5(int) = Store : &:r279_4, r279_2 +# 279| m279_6(Point) = Chi : total:m276_10, partial:m279_5 +#-----| Goto -> Block 2 + +# 281| Block 2 +# 281| m281_1(int) = Phi : from 0:m276_5, from 1:m279_5 +# 281| m281_2(Point) = Phi : from 0:m276_10, from 1:m279_6 +# 281| r281_3(glval) = VariableAddress[x] : +# 281| r281_4(glval) = VariableAddress[a] : +# 281| r281_5(glval) = FieldAddress[x] : r281_4 +# 281| r281_6(int) = Load : &:r281_5, m281_1 +# 281| m281_7(int) = Store : &:r281_3, r281_6 +# 282| v282_1(void) = NoOp : +# 275| v275_12(void) = ReturnVoid : +# 275| v275_13(void) = UnmodeledUse : mu* +# 275| v275_14(void) = AliasedUse : ~m277_5 +# 275| v275_15(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected index f6a222354fc8..1e78ae87f40f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity_unsound.expected index f6a222354fc8..1e78ae87f40f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity_unsound.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index ee75a2f28e46..f7e755964687 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -269,4 +269,14 @@ void *MallocAliasing(void *s, int size) { void *buf = malloc(size); memcpy(buf, s, size); return buf; -} \ No newline at end of file +} + +Point *pp; +void EscapedButNotConflated(bool c, Point p, int x1) { + Point a = {}; + pp = &a; // `a` escapes here and therefore belongs to the aliased vvar + if (c) { + a.x = x1; + } + int x = a.x; // The phi node here is not conflated +} diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index fb7a6eccc15b..11e502f54f1c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -1188,3 +1188,53 @@ ssa.cpp: # 268| v268_14(void) = UnmodeledUse : mu* # 268| v268_15(void) = AliasedUse : ~mu268_4 # 268| v268_16(void) = ExitFunction : + +# 275| void EscapedButNotConflated(bool, Point, int) +# 275| Block 0 +# 275| v275_1(void) = EnterFunction : +# 275| mu275_2(unknown) = AliasedDefinition : +# 275| mu275_3(unknown) = InitializeNonLocal : +# 275| mu275_4(unknown) = UnmodeledDefinition : +# 275| r275_5(glval) = VariableAddress[c] : +# 275| m275_6(bool) = InitializeParameter[c] : &:r275_5 +# 275| r275_7(glval) = VariableAddress[p] : +# 275| m275_8(Point) = InitializeParameter[p] : &:r275_7 +# 275| r275_9(glval) = VariableAddress[x1] : +# 275| m275_10(int) = InitializeParameter[x1] : &:r275_9 +# 276| r276_1(glval) = VariableAddress[a] : +# 276| mu276_2(Point) = Uninitialized[a] : &:r276_1 +# 276| r276_3(glval) = FieldAddress[x] : r276_1 +# 276| r276_4(int) = Constant[0] : +# 276| mu276_5(int) = Store : &:r276_3, r276_4 +# 276| r276_6(glval) = FieldAddress[y] : r276_1 +# 276| r276_7(int) = Constant[0] : +# 276| mu276_8(int) = Store : &:r276_6, r276_7 +# 277| r277_1(glval) = VariableAddress[a] : +# 277| r277_2(Point *) = CopyValue : r277_1 +# 277| r277_3(glval) = VariableAddress[pp] : +# 277| mu277_4(Point *) = Store : &:r277_3, r277_2 +# 278| r278_1(glval) = VariableAddress[c] : +# 278| r278_2(bool) = Load : &:r278_1, m275_6 +# 278| v278_3(void) = ConditionalBranch : r278_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 279| Block 1 +# 279| r279_1(glval) = VariableAddress[x1] : +# 279| r279_2(int) = Load : &:r279_1, m275_10 +# 279| r279_3(glval) = VariableAddress[a] : +# 279| r279_4(glval) = FieldAddress[x] : r279_3 +# 279| mu279_5(int) = Store : &:r279_4, r279_2 +#-----| Goto -> Block 2 + +# 281| Block 2 +# 281| r281_1(glval) = VariableAddress[x] : +# 281| r281_2(glval) = VariableAddress[a] : +# 281| r281_3(glval) = FieldAddress[x] : r281_2 +# 281| r281_4(int) = Load : &:r281_3, ~mu275_4 +# 281| m281_5(int) = Store : &:r281_1, r281_4 +# 282| v282_1(void) = NoOp : +# 275| v275_11(void) = ReturnVoid : +# 275| v275_12(void) = UnmodeledUse : mu* +# 275| v275_13(void) = AliasedUse : ~mu275_4 +# 275| v275_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index fb7a6eccc15b..11e502f54f1c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1188,3 +1188,53 @@ ssa.cpp: # 268| v268_14(void) = UnmodeledUse : mu* # 268| v268_15(void) = AliasedUse : ~mu268_4 # 268| v268_16(void) = ExitFunction : + +# 275| void EscapedButNotConflated(bool, Point, int) +# 275| Block 0 +# 275| v275_1(void) = EnterFunction : +# 275| mu275_2(unknown) = AliasedDefinition : +# 275| mu275_3(unknown) = InitializeNonLocal : +# 275| mu275_4(unknown) = UnmodeledDefinition : +# 275| r275_5(glval) = VariableAddress[c] : +# 275| m275_6(bool) = InitializeParameter[c] : &:r275_5 +# 275| r275_7(glval) = VariableAddress[p] : +# 275| m275_8(Point) = InitializeParameter[p] : &:r275_7 +# 275| r275_9(glval) = VariableAddress[x1] : +# 275| m275_10(int) = InitializeParameter[x1] : &:r275_9 +# 276| r276_1(glval) = VariableAddress[a] : +# 276| mu276_2(Point) = Uninitialized[a] : &:r276_1 +# 276| r276_3(glval) = FieldAddress[x] : r276_1 +# 276| r276_4(int) = Constant[0] : +# 276| mu276_5(int) = Store : &:r276_3, r276_4 +# 276| r276_6(glval) = FieldAddress[y] : r276_1 +# 276| r276_7(int) = Constant[0] : +# 276| mu276_8(int) = Store : &:r276_6, r276_7 +# 277| r277_1(glval) = VariableAddress[a] : +# 277| r277_2(Point *) = CopyValue : r277_1 +# 277| r277_3(glval) = VariableAddress[pp] : +# 277| mu277_4(Point *) = Store : &:r277_3, r277_2 +# 278| r278_1(glval) = VariableAddress[c] : +# 278| r278_2(bool) = Load : &:r278_1, m275_6 +# 278| v278_3(void) = ConditionalBranch : r278_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 279| Block 1 +# 279| r279_1(glval) = VariableAddress[x1] : +# 279| r279_2(int) = Load : &:r279_1, m275_10 +# 279| r279_3(glval) = VariableAddress[a] : +# 279| r279_4(glval) = FieldAddress[x] : r279_3 +# 279| mu279_5(int) = Store : &:r279_4, r279_2 +#-----| Goto -> Block 2 + +# 281| Block 2 +# 281| r281_1(glval) = VariableAddress[x] : +# 281| r281_2(glval) = VariableAddress[a] : +# 281| r281_3(glval) = FieldAddress[x] : r281_2 +# 281| r281_4(int) = Load : &:r281_3, ~mu275_4 +# 281| m281_5(int) = Store : &:r281_1, r281_4 +# 282| v282_1(void) = NoOp : +# 275| v275_11(void) = ReturnVoid : +# 275| v275_12(void) = UnmodeledUse : mu* +# 275| v275_13(void) = AliasedUse : ~mu275_4 +# 275| v275_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected index f6a222354fc8..1e78ae87f40f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity_unsound.expected index f6a222354fc8..1e78ae87f40f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity_unsound.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes 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 0bdec1d0a7c7..362983363cc8 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 @@ -568,6 +568,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected index 600f06a91628..02a088752dd0 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected @@ -631,6 +631,8 @@ useNotDominatedByDefinition | try_catch.cpp:21:13:21:24 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | IR: throw_from_nonstmt | void throw_from_nonstmt(int) | | vla.c:3:27:3:30 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected index e1b028b778e9..82b5f5ccef0b 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected @@ -577,6 +577,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll index 23850ebd7ab2..94d0192fe189 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll @@ -274,6 +274,36 @@ module InstructionSanity { funcText = Language::getIdentityString(func.getFunction()) } + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr instanceof UnmodeledDefinitionInstruction + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated(Instruction instr) { + shouldBeConflated(instr) and + not instr.isResultConflated() + } + + query predicate wronglyMarkedAsConflated(Instruction instr) { + instr.isResultConflated() and + not shouldBeConflated(instr) + } + query predicate invalidOverlap( MemoryOperand useOperand, string message, IRFunction func, string funcText ) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 72c8d7e3b95a..1e84d1c34759 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -321,6 +321,17 @@ class Instruction extends Construction::TInstruction { Construction::hasModeledMemoryResult(this) } + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + /** * Gets the successor of this instruction along the control flow edge * specified by `kind`. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll index 01ca8db31046..d2f23a8c15fb 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll @@ -69,6 +69,15 @@ private module Cached { cached predicate hasModeledMemoryResult(Instruction instruction) { none() } + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof UnmodeledDefinitionInstruction + or + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + } + cached Expr getInstructionConvertedResultExpression(Instruction instruction) { exists(TranslatedExpr translatedExpr | diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll index 23850ebd7ab2..94d0192fe189 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll @@ -274,6 +274,36 @@ module InstructionSanity { funcText = Language::getIdentityString(func.getFunction()) } + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr instanceof UnmodeledDefinitionInstruction + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated(Instruction instr) { + shouldBeConflated(instr) and + not instr.isResultConflated() + } + + query predicate wronglyMarkedAsConflated(Instruction instr) { + instr.isResultConflated() and + not shouldBeConflated(instr) + } + query predicate invalidOverlap( MemoryOperand useOperand, string message, IRFunction func, string funcText ) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 72c8d7e3b95a..1e84d1c34759 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -321,6 +321,17 @@ class Instruction extends Construction::TInstruction { Construction::hasModeledMemoryResult(this) } + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + /** * Gets the successor of this instruction along the control flow edge * specified by `kind`. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 0d158023ad49..155934689b6c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,6 +65,29 @@ private module Cached { instruction instanceof ChiInstruction // Chis always have modeled results } + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof UnmodeledDefinitionInstruction + or + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + or + // Chi instructions track virtual variables, and therefore a chi instruction is + // conflated if it's associated with the aliased virtual variable. + exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof + Alias::AliasedVirtualVariable + ) + or + // Phi instructions track locations, and therefore a phi instruction is + // conflated if it's associated with a conflated location. + exists(Alias::MemoryLocation location | + instruction = Phi(_, location) and + not exists(location.getAllocation()) + ) + } + cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand | 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 7de1ab8d72ef..aee4959513ed 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 @@ -61,6 +61,11 @@ class MemoryLocation extends TMemoryLocation { class VirtualVariable extends MemoryLocation { } +/** A virtual variable that groups all escaped memory within a function. */ +class AliasedVirtualVariable extends VirtualVariable { + AliasedVirtualVariable() { none() } +} + Overlap getOverlap(MemoryLocation def, MemoryLocation use) { def = use and result instanceof MustExactlyOverlap or diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected b/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected index 38cb5b5f51d6..c5a0b98598f7 100644 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected +++ b/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected index 38cb5b5f51d6..c5a0b98598f7 100644 --- a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected +++ b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected @@ -16,6 +16,8 @@ lostReachability backEdgeCountMismatch useNotDominatedByDefinition switchInstructionWithoutDefaultEdge +notMarkedAsConflated +wronglyMarkedAsConflated invalidOverlap missingCanonicalLanguageType multipleCanonicalLanguageTypes